AWS SES 이메일 이벤트 로깅(Bounce, Complaint 확인)
이메일을 발송하게 되면 대부분의 경우 성공적으로 전달 되지만, 다양한 이유로 이메일 전달에 실패하곤 한다.
박스히어로의 경우 이메일 로그인시 OTP(One Time Password)를 이메일로 발송하고 있는데, 이메일을 받지 못하는 경우 로그인을 하지 못하는 상황이 되버린다 😰. 이처럼 이메일 전송 실패는 사용자 경험에 큰 영향을 주기 때문에 이메일 전송 실패의 이유를 찾아내는건 아주 중요한 일이다.
AWS SES에는 이메일 발송 결과를 이벤트 형태로 받아볼 수 있는 기능을 제공하는데 박스히어로에서 어떻게 적용했는지 써보려고 한다.
이메일 발송 실패 사례
이메일 발송을 했지만 다양한 이유로 실패할 수 있는데 크게 정리하면 다음과 같다.
Hard bounce
보통 이메일 주소가 잘못된 경우 발생하며 SES에서 발송 재시도 하지 않는다. DNS Lookup에 실패한 경우에는 재시도 하기도 한다.
Soft bounce
일시적으로 발송에 실패한 경우이며 메일함이 꽉찼다거나, 스팸성 컨텐츠, 바이러스 포함으로 인해 메일 서버가 받기를 거부한 경우에도 발생한다. Soft bounce가 발생한 경우 AWS SES는 여러번 재시도 후 발송을 중단한다.
Complaint
내가 보낸 이메일이 스팸으로 처리된 경우 피드백을 받은 경우이다. 이메일 보낸후 몇일 후 도착할수도 있다.
그외 SES관련 이슈
SES API 콜은 성공적으로 했지만 발송에 실패한 경우이다. SES에는 Sending limit이 존재하여 발송에 실패할 수 있다. 또한 SES의 Suppression List가 자동으로 관리 되는데, 여기 포함된 이메일의 경우 발송하지 않는다.
더 자세한 발송 실패 사례는 아래 링크에서 확인할 수 있다.
SES 발송 내역을 확인하는 방법
AWS SES 콘솔에는 최근 발송 건수, bounce, complaint 건수 통계 정도만 확인이 가능하고 개별 이메일의 발송 로그를 확인할수 없다 😅
다행히 AWS SES는 개별 이메일 발송 내역을 SNS 이벤트로 보내주는 기능을 제공한다!
SNS 이벤트를 받는 방법은 2가지로 나뉜다.
방법 1: Notification
이메일 발송 도메인(bgpworks.com) 또는 발송 이메일 주소(support@bgpworks.com) 마다 Notification을 설정할 수 있다. Bounce, Complaint, Delivery 세가지 이벤트를 SNS로 보내준다.
Notification으로 발송 성공 또는 실패를 확인할수는 있지만, 발송 시도 Event는 받을수는 없기 때문에 아래 설명한 Event 방식으로 구현했다.
방법2: Event
Event는 SES의 Configuration Set마다 설정할수 있으며, Notification보다 다양한 종류의 Event를 SNS 이벤트로 형태로 발생시킬 수 있다.
Bounce
, Complaint
, Delivery
, Send
, Reject
, Open
, Click
, Rendering Failure
, DeliveryDelay
발송 시도, 발송 delay, 메일 오픈, 메일 click 등의 이벤트까지 확인이 가능하여 이메일 발송 관련 이슈 파악에 훨씬 더 적합하다고 볼 수 있다.
Event는 SES의 Configuration Set마다 설정이 가능한 구조이기 때문에 당연히 Configuration Set을 먼저 생성해야 한다.
SES sendEmail API에서 해당 Configuration Set을 사용하도록 파라메터를 넘겨 발송해야 정상적으로 처리 된다.
아키텍처
SES는 이벤트를 SNS로 쏴주는 역할까지만 해주고, 이 이벤트를 어디에 어떻게 저장할지는 우리의 몫이다. 우리는 AWS Lambda, Dynamodb 를 활용하여 저장하는 방법으로 구현했다.
SES에서 발생된 이벤트는 SNS로 들어가고, SNS는 Lambda를 Trigger하여 결국 DynamoDB에 모든 이벤트를 저장한다. Lambda, DynamoDB 조합을 사용하는 이유는 우선 서버 관리를 할 필요 없다는 점과 아무리 많은 이벤트가 발생되더라도 자동 스케일 가능하다는 점 때문이다. 덤으로 DynamoDB 에는 TTL기능도 있어 오래된 로그를 자동으로 지울수 있다.
DynamoDB 특성상 복잡한 Query가 불가능하지만, 이번에 작성하는 이벤트 로그용으로는 특정 이벤트 확인, 특정 이메일의 이벤트 확인만 가능하면 되어 DynamoDB를 사용하기 충분했다.
Lambda 코드는 약 40줄로 간단한데, SNS로 들어온 Event전체를 DynamoDB에 넣는 역할만 한다.
SES API Call 이후 받는 Messsage Id를 Partition Key로 하고, Sns Publish Time을 Sort Key로 하여 SES 발송건별 이벤트를 확인할 수 있다.
- Partition Key : SES Message Id
- Sort Key : Sns Publish Time
추가로 Global Secondary Index를 추가했는데, 수신 이메일별로 이벤트를 쿼리해볼 수 있다.
- Partition Key : Destination Email
- Sort Key : Sns Publish Time
테스트를 위한 팁
SES에는 Bounce, Complaint등 테스트를 위한 이메일 주소를 제공해준다. 아래의 이메일로 이메일을 보내면 Delivery, Bounce, Complaint 이벤트가 어떻게 들어오는지 확인이 가능하다!
- success@simulator.amazonses.com
- bounce@simulator.amazonses.com
- complaint@simulator.amazonses.com
Mailbox Simulator에 관한 자세한 내용은 아래 링크에서 확인 가능하다.
결과
위와 같이 특정 이메일에 보낸 메일의 이벤트를 확인할수 있다. 위 예제를 보면 Send, Delivery, Open 이벤트 순으로 잘 들어왔다. message 필드에는 받은 이벤트 전체를 Dump해 두었는데, 추후 Bounce, Complaint 이벤트가 발생하면 상세 내역을 확인해 볼수 있다.
Send이벤트가 없다면 발송 시도 조차 하지 못한 케이스이고, Send는 있지만 Delivery가 없다면 Suppression List에 포함되어 발송 처리 되지 않은 이메일로 유추해 볼수 있다.
아직 로그 확인이 가능한 Fancy한 웹사이트는 없지만, 간혹 이슈가 생겼을때 그 이유를 파악 할수 있는 기반은 준비되었다. 추후 비개발 직군이 빠르게 확인해야 하는 상황이 많이지면 DynamoDB에 있는 로그를 웹사이트에서 확인할 수 있게 추가할 예정이다.
참고 자료
- https://docs.aws.amazon.com/ses/latest/dg/event-publishing-retrieving-sns.html
- https://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity-using-notifications.html
- https://aws.amazon.com/premiumsupport/knowledge-center/lambda-sns-ses-dynamodb/
- https://medium.com/schibsted-engineering/building-a-serverless-email-delivery-service-on-aws-9269f55533a0