AWS SES SMTP를 활용한 대량 메일 전송 시스템 정리

Jeongkuk Seo
sjk5766
Published in
14 min readFeb 15, 2020

최근에 회사에서 대량으로 메일을 전송하는 기능을 만들어야 했습니다. 하루에 10,000건 이상의 메일을 전송해야 했는데 결론적으로 AWS에서 제공하는 SES(Simple Email Service)를 사용했고, 이에 대해 정리 하겠습니다.

기존 프로그램은 Google에서 제공하는 SMTP 를 통해 메일을 전송하고 있었습니다. 허나 찾아보니 일반 계정은 24시간 기준 500건, 유료 계정인 G suite은 24시간 기준 2000건 밖에 전송이 되지 않았습니다.

여기서 24시간 기준을 설명하면, 일반 계정으로 밤 11시에 메일 500건을 보냈을 때, 다음 날 밤 11시에 500건을 보낼 수 있습니다. 새벽 12시를 기준으로 초기화 되는 것이 아니라 정말 24시간 뒤에 보낼 수 있습니다.

회사에서 유료 계정인 G suite을 사용하는데 실제로 테스트 해보면 2000건 까지 전송하는 것을 확인할 수 있었습니다. 만약 이를 초과해서 메일을 보내면, GMail에 로그인 해서 메일을 보낼 경우, limit을 초과했다는 메일을, 시스템적으론 limit을 초과했다는 데이터를 응답받습니다.

Gmail로 메일 초과 시, 실패 응답 메일

AWS에서 제공하는 SES를 통해 대량의 메일을 발송할 수 있다는 정보를 찾곤 해당 방법으로 접근했습니다. 이 절차를 간단하게 풀어보면 아래와 같습니다.

  1. AWS console에서 SES 서비스를 들어가 email, domain 인증을 받기 위한 절차를 수행합니다.
  2. AWS console에서 프로그램이 원하는 만큼 메일을 보내기 위해 AWS에 sandbox를 해제 해 달라고 요청합니다.
  3. Google이 제공하는 SMTP 서버 대신, SES가 제공하는 SMTP 서버의 계정을 생성합니다.
  4. 반송률을 신경써야 하므로 AWS SNS를 통해 반송 된 메일에 대한 주제와 구독을 생성하고 프로그램이 반송된 메일을 구독할 수 있는 절차를 수행합니다.
  5. 프로그램은 SES가 제공하는 SMTP를 통해 원하는 만큼 메일을 발송하되, 반송된 메일에게는 보내지 않는 처리를 수행한다.

디테일한 절차

아래부터 위 1~5번 절차에 대한 상세 내용을 다룹니다. 우선 AWS Asia Region에는 SES 서비스를 제공하는 Region이 없습니다. 저는 Oregon 리전을 선택했습니다.

1. Email 및 Domain 등록

AWS Console에 로그인 한 후, SES 서비스에 접속한 뒤 Email Addresses 버튼을 클릭합니다. 아래처럼 Verify a New Email Address를 클릭합니다.

클릭하면 아래 Email Address 부분에 메일 전송 시 사용할 메일을 등록합니다.

Verify This Email Address를 클릭하면 아래와 같은 화면이 나옵니다.

등록한 메일 계정에 아래와 같은 메일이 올 겁니다. 아래 사진에는 없지만 메일 중간에 있는 링크를 클릭합니다.

메일의 일부

위에서 링크를 클릭하면 AWS console에서 메일이 아래와 같이 verify 상태로 변경됩니다.

이젠 도메인을 등록할 차례입니다. Domains 버튼을 클릭 후 Verify a New Domain 버튼을 클릭합니다.

발송하는 메일이 test@test.com 이라고 가정할 때 도메인은 test.com 이 됩니다. 위에서 Email Addresses 에서 등록한 메일의 도메인을 입력합니다. 발송하는 메일의 신뢰성을 높이기 위해 DKIM setting도 클릭합니다.

위 그림에서 Verify This Domain 버튼을 클릭하면 아래와 같이 Domain 정보를 담은 표가 보입니다.Domain Verification Record DKIM Record Set 정보 모두 저장해 둡니다.

현 상태에서 Domain 버튼을 보면 아직 pending 상태가 된 것을 확인할 수 있습니다. DNS 서버에 위에서 저장한 정보를 추가해 줘야 verify 상태로 넘어갑니다.

만약 사용하는 Domain 다른 회사에서 제공하는 DNS라면 해당 업체에 정보 추가를, AWS DNS 53을 사용하고 있다면 손수 셋팅하시면 됩니다. 제 경우 AWS에서 제공하는 DNS 53을 사용하고 있습니다.

AWS Console에서 DNS 53 서비스로 이동합니다. 왼쪽에 호스팅 영역을 누른 뒤 레코드 세트 생성 버튼을 누릅니다. 그럼 오른쪽에 이름, 유형, 이 3 부분을 입력해야 합니다. 각각 위에서 Domain Verify 과정에서 저장해 둔 Name, Type, Value 값과 매핑되니 해당 값을 입력하고 생성합니다.

정상적으로 해당 값을 DNS에 추가 했다면 얼마 지나지 않아 SES Domain 버튼에서 아래와 같이 verified 상태로 전환됩니다.

2. SandBox 해제

이제 AWS 측에 sandbox 환경에서 벗어나 원하는 만큼 메일을 전송해 달라고 부탁할 차례입니다.

AWS Console SES에서 아래와 같이 Sending Statistics 버튼을 클릭하고 Request a Sending Limit Increase 버튼을 클릭합니다.

클릭한 뒤 아래와 같이 Service limit increase 버튼을 클릭합니다.

상세한 설정 내용을 보겠습니다. 아래와 같이 적용하시면 되며, Mail Type과 Website URL은 각각 상황에 맞게 입력하면 됩니다.

SES를 생성한 Region을 입력하고 Limit에는 Daily Sending Quota를 입력합니다. 이게 의미하는 것은 하루에 전송할 양을 늘리겠다는 의미입니다. New limit Value에는 전송하길 원하는 양을 입력하면 됩니다. 제 경우 하루에 50,000건은 넘지 않는다고 판단하고 적었습니다. Case Description 부분에는 어떻게 활용할지를 적으면 됩니다.

3. SMTP 계정 정보 생성

위 내용을 작성하고 AWS에 Request 했다면 처리되기 까지 최대 24시간이 걸립니다. 그 동안 AWS Console SES에서 SMTP Settings 버튼을 클릭하고 아래 보이는 Create My SMTP Credentials 버튼을 선택합니다. 그럼 ID와 PWD 정보를 얻을 수 있고 이는 추후에 프로그램이 SMTP에 접속하는데 명시하면 됩니다.

4. 반송률 조절을 위한 SNS 생성

이젠 SES에서 반송률을 조절하기 위한 작업을 해야 합니다. 반송률은 반송 메일에 의해 달라지는데 이유는 존재하지 않는 메일인 경우가 많습니다. 이 반송률이 5~10%가 되면 AWS측에서 경고를 주고 추후에는 서비스가 아닌 AWS 계정 자체를 Block 합니다. 따라서 SES 서비스를 사용할 때 꼭 필요한 절차입니다.

AWS Console에서 SNS을 클릭하고 아래 그림과 같이 다음 단계 버튼을 클릭합니다.

이름만 지정하고 주제 생성 버튼을 클릭합니다. 필요한 셋팅이 있으면 추가하시면 됩니다.

주제가 생성되면 아래와 같이 구독을 생성합니다. 구독으로 생성한 서비스에 대해 반송된 메일이 전송됩니다.

구독 생성 버튼을 누르면 아래와 같이 프로토콜을 선택할 수 있습니다. 제 경우 테스트 서비스가 HTTP로 동작하므로 HTTP를 선택하고 엔드 포인트에 동작하는 서비스의 도메인을 넣었습니다. 그 후 구독생성 버튼을 클릭합니다.

위 프로토콜을 자세히 보면 아래와 같습니다. HTTP/HTTPS 서비스부터, 개인 이메일이나 Lambda까지 선택할 수 있습니다.

위 절차에서 구독을 생성하면 위에서 엔드 포인트에서 입력했던 URL로 아래와 같은 JSON 데이터가 전송됩니다. 이 데이터는 구독을 받겠냐고 물어보는 데이터로 정상적으로 받게 하기 위해 SubscribeURL에 매핑되는 URL 주소에 접속해야 합니다.

수동으로는 해당 URL을 그냥 브라우져에서 입력해도 되고, 제 경우 프로그램이 JSON 데이터를 파싱하여 Type 값이 SubscriptionConfirmation일 경우 curl로SubscribeURL에 명시된 주소로 접속하게 했습니다.

POST / HTTP/1.1
x-amz-sns-message-type: SubscriptionConfirmation
x-amz-sns-message-id: 165545c9–2a5c-472c-8df2–7ff2be2b3b1b
x-amz-sns-topic-arn: arn:~~~:~~~:MyTopic
Content-Length: 1336
Content-Type: text/plain; charset=UTF-8
Host: example.com
Connection: Keep-Alive
User-Agent: Amazon Simple Notification Service Agent

{
"Type” : "SubscriptionConfirmation",
"MessageId" : "165545c9–2a5c-472c-8df2–7ff2be2b3b1b",
"Token" : "2336412f37f…",
"TopicArn" : "arn:~~~:~~~:MyTopic",
"Message" : "You have chosen to subscribe to the topic arn:~~~:~~~:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:~~~:~~~:MyTopic&Token=~~...",
"Timestamp" : "2012–04–26T20:45:04.751Z",
"SignatureVersion" : "1",
"Signature" : "EXAMPLEpH+…",
"SigningCertURL" : "https://sns.~~~~"
}

해당 URL에 접속하게 되면 구독 정보가 아래와 같이 확인됨으로 변경됩니다.

만약 프로토콜을 HTTP가 아닌 이메일로 설정했다면 아래와 같이 메일을 받게 됩니다. 이 때 Confirm subscription 버튼을 클릭하면 확인됨으로 변경됩니다.

위에서 한 작업들을 풀어보면 Email or HTTP 원하는 프로토콜을 이용하여 구독을 받겠다는 의미입니다. 이제 SES랑 SNS를 연동하겠습니다.

AWS Console에서 다시 SES로 돌아가 Email Addresses 버튼을 클릭 후 우리가 등록한 이메일을 클릭합니다. 그러면 아래와 같이 Notifications아래에 Bounce Notifications SNS Topic 이 보입니다. Edit Configuration 버튼을 클릭 한 후 우리가 생성한 Topic으로 변경합니다.

Bounce는 영어로 반송을 의미하며 반송되는 메일은 해당 Topic과 연결된 Subscription을 통해 우리가 셋팅한 프로토콜로 전달 될 것입니다.

2번 SandBox 해제 절차에서 했던 요청에 대해 24시간 내에 AWS측에서 아래와 같은 메일이 올 겁니다.

Limit increase 메일 일부

AWS Console에서 SES에 들어가서 Sending Statistics 버튼을 클릭하면 요청한 대로 limit이 수정된 것을 알 수 있습니다.

5. 반송된 메일 테스트 및 처리

이 상태에서 Email Addresses 버튼을 누른 후 Email을 클릭하고 위의 Send a Test Email 버튼을 클릭합니다. To 부분에 아래 이메일을 입력합니다.

bounce@simulator.amazonses.com

위 이메일을 AWS가 제공하는 반송 테스트용 이메일로, 반송률에 영향을 미치지 않습니다.

현재까지 정상적으로 돌아간다면 Email로는 아래와 같은 메일이 옵니다.

반송 될 경우 메일로 notification 내용

만약 수동으로 반송된 메일에게 전송하지 않도록 한다면 위처럼 이메일로 데이터를 받아 처리를 하면 됩니다.

시스템으로 처리하고 싶을 경우 프로토콜을 HTTP or HTTPS로 선택하고 입력한 엔드포인트에 아래와 같은 데이터가 전송됩니다.

{
“Type” : “Notification”,
“MessageId” : “a99c8e87–8ba8–5dcf-b349-d832327509dd”,
“TopicArn” : “arn:aws:sns:us-west-2:883354994517:ses_bounce_topic”,
“Message” : “{\”notificationType\”:\”Bounce\”,\”bounce\”:{\”bounceType\”:\”Permanent\”,\”bounceSubType\”:\”General\”,\”bouncedRecipients\”:[{\”emailAddress\”:\”52369582759@mement.net\

우리는 위 데이터에서 bouncedRecipients의 emailAddress를 추출하여 필터링 하면 됩니다. 제 경우 반송된 메일을 DB에 insert 하고, 메일 계정 중 DB에 등록된 계정이 있으면 보내지 않는 방식으로 처리했습니다.

마지막으로

위 절차를수행해도, 제가 보내려는 mail이 고객들의 inbox로 전달될 지는 확신할 수 없습니다. 인터넷 상에서 바이러스, 스팸 메일을 차단하기 위해 제가 보낸 메일이 차단되거나 spam mail로 분류될 수 있습니다. 신뢰성을 높여 메일이 inbox로 가는 확률을 높이기 위해 SPF나 DKIM 같은 셋팅도 하지만, 대량으로 보낼 때 메일 리스트에 자신의 메일 계정을 넣어 정상적으로 들어오는지 한번은 확인 하셔야 합니다. 왜냐면 테스트 시에는.. 10,000개 가량의 메일을 구할 수 없기 때문에..ㅜㅜ

--

--