JAVA/프로젝트

알림 기능 개발, AWS SNS 연동

whyHbr 2024. 11. 21. 22:46
728x90
반응형

Spring Boot 내에서 이벤트, 경고, 에러 같은 중요한 상황을 감지하고 이를 관리자, 개발자에게 알리는 기능. 신속한 대응과 문제 해결을 돕는 데 중요한 역할을 한다.

  • 장애 감지, 대응
  • 서비스 가용성 유지
  • 성능 모니터링
  • 비용 절감
  • 사용자 경험 향상
  • 예방적 조치

AWS SNS 연동

Amazon Simple Notification Service: AWS의 클라우드 기반 메시징 서비스. 이를 사용하면 애플리케이션, 서비스 또는 시스템 간에 다양한 종류의 메세지를 안전하게 전송하고 관리 할 수 있다.

  • 푸시 알람
  • 다중 프로토콜 지원 (HTTP, HTTPS, SMS,email, SQS, Lambda 등 지원)
  • 이벤트 기반 아키텍쳐
  • 확장성과 신뢰성
  • 미리 알림 및 모니터링 

 

연동하기

AWS root 사용자로 접속 - IAM dashboard 접속 - Permissions policies(SNS 검색해서 나오는 3개 선택), Access Key(third party service) 생성

Simple Notification Service - topic 생성

 

properties 파일에 설정값 추가. 

sns.topic.arn=arn:
aws.accessKey= accesskey
aws.secretKey= secret key
aws.region=ap-northeast-2 #서울
cloud.aws.region.static=ap-northeast-2 #서울
cloud.aws.stack.auto=false #저장소에 저장하지 않음

 

AWSConfig class 생성.


import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
@Getter
public class AWSConfig {

    @Value("${sns.topic.arn}")
    private String sosTopicARN;

    @Value("${aws.accessKey}")
    private String accessKey;

    @Value("${aws.secretKey}")
    private String secretKey;

    @Value("${aws.region}")
    private String awsRegion;

}

 

 

SnsService


import software.amazon.awssdk.regions.Region;

import com.example.boardserver.config.AWSConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.services.sns.SnsClient;

@Service
@RequiredArgsConstructor
public class SnsService {

    private final AWSConfig awsConfig;

    //aws 인증 객체를 만든다.
    public AwsCredentialsProvider getAwsCredentials(String accessKeyId, String secretAccessKey) {
        AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(accessKeyId,
                secretAccessKey);
        return() -> awsBasicCredentials;
    }

    public SnsClient getSnsClient(){
        return SnsClient.builder()
                .credentialsProvider(
                        getAwsCredentials(awsConfig.getAccessKey(), awsConfig.getSecretKey())
                ).region(Region.of(awsConfig.getAwsRegion())) //실제 sns config 에 있는 region 값을 가져옴
                .build();
    }
}

 

 

SnsController


@RestController
@RequiredArgsConstructor
@Log4j2
public class SnsController {

    private final SnsService snsService;
    private final AWSConfig awsConfig;

    @PostMapping("/create-topic")
    public ResponseEntity<String> createTopic(@RequestParam final String topicName) {
        final CreateTopicRequest createTopicRequest = CreateTopicRequest.builder()
                .name(topicName)
                .build();

        SnsClient snsClient = snsService.getSnsClient();
        final CreateTopicResponse createTopicResponse = snsClient.createTopic(createTopicRequest);

        if (!createTopicResponse.sdkHttpResponse().isSuccessful()) {
            throw getResponseStatusException(createTopicResponse);
        }
        log.info("topic name : {} ", createTopicResponse.topicArn());
        log.info("topic list: {}", snsClient.listTopics());
        //자원 반납
        snsClient.close();
        return new ResponseEntity<>("TOPIC CREATING SUCCESSFULLY", HttpStatus.CREATED);
    }

    //구독
    @PostMapping("/subscribe")
    public ResponseEntity<String> subscribe(@RequestParam final String endPoint,
            @RequestParam final String topicArn) {
        final SubscribeRequest subscribeRequest = SubscribeRequest.builder()
                .protocol("https")
                .topicArn(topicArn)
                .endpoint(endPoint)
                .build();

        SnsClient snsClient = snsService.getSnsClient();
        final SubscribeResponse subscribeResponse = snsClient.subscribe(subscribeRequest);

        if(!subscribeResponse.sdkHttpResponse().isSuccessful()){
            throw getResponseStatusException(subscribeResponse);
        }
        log.info("topicARN to subscribe = " + subscribeResponse.subscriptionArn());
        log.info("subscription list = " + snsClient.listSubscriptions());
        snsClient.close();
        return new ResponseEntity<>("TOPIC SUBSCRIBE SUCCESS", HttpStatus.OK);
    }

    //발행
    @PostMapping("publish")
    public String publish(@RequestParam final String topicArn,
            @RequestBody Map<String, Object> message) {
        SnsClient snsClient = snsService.getSnsClient();
        final PublishRequest publishRequest = PublishRequest.builder()
                .topicArn(topicArn)
                .subject("HTTP ENDPOINT TEST MESSAGE")
                .message(message.toString())
                .build();
        PublishResponse publishResponse = snsClient.publish(publishRequest);
        log.info("message status:" + publishResponse.sdkHttpResponse().statusCode());
        snsClient.close();

        return "sent MSG ID = " + publishResponse.messageId();
    }

    private ResponseStatusException getResponseStatusException(SnsResponse snsResponse) {
        return new ResponseStatusException(
                HttpStatus.INTERNAL_SERVER_ERROR, snsResponse.sdkHttpResponse().statusText().get()
        );
    }
}

topic create POST

 

notiy2 생성 확인.

 

board-server2 arn 을 사용해 구독하고 발행해보자.'

endPoint는 webhook 에서.

topicArn 은 notiy2 arn 사용

 

요청 성공시 웹훅에 SubscribeURL 이 온다. 이 URL 바탕으로 실제 메세지를 발행할 수 있다. 

 

 

url 들어가면 arn 값이 나옴.

 

웹훅을통해 알람이 온다.

 

 

728x90