✨ 구현 할 기능
현재 SES API 호출 성공 시점을 SENT로 처리하고 있어, 실제 전달 실패(soft bounce, hard bounce, complaint 등)를
시스템이 인지하지 못하는 문제가 있음.
SES → SNS → SQS 경로를 통해 실제 이메일 전달 결과 이벤트를 수신하고, notification_history 상태를 정확하게 반영하는 구조로 개선.
📢 구현 방식
SES → SNS → SQS → Spring Boot 폴링 방식 고려
SES ──(Delivery/Bounce/Complaint 이벤트)──> SNS Topic
└──> SQS Queue <── Spring Boot (@Scheduled 폴링)
└──> DLQ (처리 실패 시)
방법 선택 근거: 기존 @Scheduled 패턴과 일관성 유지, Spring 서비스 레이어 재사용,
앱 다운 시 SQS 메시지 보관으로 이벤트 유실 방지.
상태 모델 확장 (PENDING/SENT/FAILED → 5가지)
| 상태 |
의미 |
PENDING |
SES 미접수 (초기 생성) |
ACCEPTED |
SES API 호출 성공, Delivery 이벤트 대기 중 |
SENT |
SES Delivery 이벤트 수신 확인 |
FAILED |
Soft bounce / ACCEPTED 타임아웃 (재시도 대상) |
BLOCKED |
Hard bounce / Complaint (재시도 차단) |
📑 기능 명세
인프라
DB
애플리케이션
테스트
📕 래퍼런스
✨ 구현 할 기능
현재 SES API 호출 성공 시점을
SENT로 처리하고 있어, 실제 전달 실패(soft bounce, hard bounce, complaint 등)를시스템이 인지하지 못하는 문제가 있음.
SES → SNS → SQS 경로를 통해 실제 이메일 전달 결과 이벤트를 수신하고,
notification_history상태를 정확하게 반영하는 구조로 개선.📢 구현 방식
SES → SNS → SQS → Spring Boot 폴링 방식 고려
상태 모델 확장 (PENDING/SENT/FAILED → 5가지)
PENDINGACCEPTEDSENTFAILEDBLOCKED📑 기능 명세
인프라
DB
notification_history.statusENUM에ACCEPTED,BLOCKED추가notification_history.ses_message_id컬럼 추가 (이벤트 매핑용)ses_message_id인덱스 추가애플리케이션
NotificationStatusenum에ACCEPTED,BLOCKED추가NotificationHistory엔티티에sesMessageId필드 추가EmailSender.send()반환 타입 변경 (void→String, messageId 반환)EmailSender—SendEmailRequest에 Configuration Set 이름 추가SingleReviewEmailSender— 발송 성공 시SENT→ACCEPTED+ messageId 저장으로 변경NotificationHistoryRepository—updateStatusAndSesMessageId(),findAllBySesMessageId(),updateStatusWhereDeadlineExpired()쿼리 추가AwsSqsConfig—SqsClient빈 등록SesEventPoller—@ScheduledSQS 폴링, 처리 성공 시deleteMessage()SesEventProcessingService— Delivery/Bounce/Complaint 이벤트 분기 처리AcceptedTimeoutService— deadline 초과ACCEPTED→FAILED전환 스케줄러build.gradle—software.amazon.awssdk:sqs의존성 추가테스트
SesEventProcessingServiceTest— 이벤트별 상태 변경 검증 (Delivery/Bounce hard&soft/Complaint)SingleReviewEmailSenderTest— 발송 성공 시ACCEPTED상태 검증으로 수정📕 래퍼런스