본문 바로가기

AWS

AWS SES(Simple Email Service) 사용법과 Spring Boot에서 SES 사용하기

🔐 들어가며

안녕하세요! 이번 포스팅에서 AWS에서 제공해주는 이메일 발송 서비스인 Simple Email Service(SES)의 사용법과 Spring Boot에서는 어떻게 사용하는지 소개해드리려고 합니다.


📚 AWS SES 사용하기

서울 리전(ap-northeast-2) 기준으로 작성된 포스팅입니다.

 

1. SandBox 상태 해제하기

처음에 AWS에서 이메일을 등록한다고 바로 사용할 수 있는게 아닙니다. 초기 상태는 'SandBox'라는 상태인데, 이 상태일 때에는 AWS SES를 사용할 수 없습니다.

Sending Statistics에 들어가면, 현재 상태가 Sandbox 상태인 것을 확인할 수 있습니다.

Sandbox인 상태

Sandbox상태를 해제하기 위해서는 Edit your account details를 눌러 웹사이트 URL, 사용 방법 등을 적어놓고 제출을 하면 됩니다.

기본 발송량은 하루 200건 제한인데, Sandbox 상태 해제 + 200건 제한을 늘리고 싶다면 위 방법이 아닌 고객센터에 case를 등록하면 됩니다. 단, 제한을 늘릴 때에는 이메일을 보내는 빈도, 수신자 목록 관리 방법, 구독 해제 방법, 이메일 템플릿 같은 정보를 추가로 기재해야합니다.

제출일 기준으로 1-2일이 지나면 Sandbox 상태가 해제되었다는 메일이 도착하게 되고, 확인을 해보면

상태가 'Sandbox'에서 'Enabled'로 바뀐 것을 확인할 수 있습니다! 저는 제한을 늘렸기 때문에 하루에 50000건까지 발송이 가능하게 되었습니다.

 

2. 이메일 주소 등록하기

이메일 주소를 등록하는 방법은 정말 간단합니다.

이메일 주소 목록에 간 뒤에, Verify a New Email Address를 클릭해서 이메일 주소를 등록할 수 있습니다.

이메일 주소를 등록하게 되면 인증 요청 메일이 도착하게 되고,

 

인증 요청 메일

메일함에 가면 이메일 인증 링크를 확인할 수 있습니다. 인증 링크를 눌러 인증이 완료되면

 

verified 상태

아래와 같이 verified 상태로 변경된 것을 확인할 수 있습니다. 테스트 이메일을 보내볼까요?

 

To, Subject, Body에 순서대로 내용을 적어주고, Send Test Email을 클릭하면

 

메일이 잘 도착하는 것을 확인할 수 있습니다.

 

3. 도메인 등록하기

이메일 주소를 등록해봤으니 이번에는 도메인을 등록해볼까요?

도메인 목록에 간 뒤에, Verify a New Domain을 클릭해 도메인을 추가할 수 있습니다.

도메인 생성이 되었습니다! Route53에 가면 해당 이름으로 도메인들이 생성된 것을 확인할 수 있습니다. 그 이후에 Route53에서 새로고침을 눌러주고 도메인 주소 목록에서 새로고침을 눌러주면

 

verified 상태로 변경된 것을 확인할 수 있습니다. 이번에도 테스트 메일을 보내볼까요?

 

아까와 같은 방법으로 테스트 메일을 보내보도록 하겠습니다.

 

이번에도 메일이 잘 온걸 확인할 수 있습니다. 하지만 이번에는 인증기관이 설정해둔 도메인으로 바뀐 것을 확인할 수 있습니다.


🚀 Spring Boot에서 SES 사용하기

AWS에서는 aws-java-sdk-ses라는 라이브러리를 지원해주기 때문에, 이 라이브러리를 사용하면 간단하게 Spring Boot에서 SES를 사용할 수 있습니다.

1. 액세스키 발급받기

가장 먼저 해당 라이브러리가 저희의 SES에 접근할 수 있도록 하기 위해 액세스키를 발급 받아야합니다.

내 프로필을 누르고 보안 자격 증명을 들어갑니다.

 

새로운 액세스 키를 만들어줍니다.

 

액세스키가 생성된 이후에 토글을 클릭하면 액세스 키 ID와 보안 액세스 키를 확인할 수 있습니다. 이를 잘 적어두거나 키 파일을 다운로드해서 액세스 키와 보안 액세스 키의 정보를 저장해둡니다. 이 때, 액세스 키와 보안 액세스 키는 절대로 외부에 유출되지 않게 주의해야합니다.

 

2. 의존성 추가

build.gradle

dependencies {
    ...
    compile 'com.amazonaws:aws-java-sdk-ses:1.12.3'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

의존성을 추가해줍니다.

 

3. 설정 파일에 키 정보 저장

yml파일 혹은 properties 파일에 aws.ses.access-key와 secret-key 정보를 설정해줍니다.

application.yml

aws:
  ses:
    access-key: ADGRCXVB # 액세스 키 ID
    secret-key: dslkfmldfan # 보안 액세스 키

이 때, 여기에 입력하는 정보들은 절대 외부로 유출되면 안되는 정보이기 때문에 Github에 올리지 않게 주의해야합니다. 만약 Github에 올릴 때에는 gitignore로 따로 빼서 관리해야합니다. 

 

4. SES 설정 파일

AwsSesConfig.java

@Configuration
public class AwsSesConfig {

  @Value("${aws.ses.access-key}")
  private String accessKey;

  @Value("${aws.ses.secret-key}")
  private String secretKey;

  @Bean
  public AmazonSimpleEmailService amazonSimpleEmailService() {
    final BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
    final AWSStaticCredentialsProvider awsStaticCredentialsProvider = new AWSStaticCredentialsProvider(
        basicAWSCredentials);

    return AmazonSimpleEmailServiceClientBuilder.standard()
        .withCredentials(awsStaticCredentialsProvider)
        .withRegion("ap-northeast-2")
        .build();
  }

}

Config 파일을 통해 AWS SES에 접근할 수 있게 권한을 설정해주고, 리전도 설정해줍니다. 저는 서울 리전에서 진행중이기 때문에 ap-northeast-2을 입력했습니다.

 

5. 이메일 전송용 DTO

EmailSenderDto.java

@Getter
public class EmailSenderDto {

  public static final String FROM_EMAIL = "ajufresh@gmail.clom"; // 보내는 사람

  private final List<String> to; // 받는 사람
  private final String subject; // 제목
  private final String content; // 본문

  @Builder
  public EmailSenderDto(final List<String> to, final String subject,
      final String content) {
    this.to = to;
    this.subject = subject;
    this.content = content;
  }

  public SendEmailRequest toSendRequestDto() {
    final Destination destination = new Destination()
        .withToAddresses(this.to);

    final Message message = new Message()
        .withSubject(createContent(this.subject))
        .withBody(new Body()
            .withHtml(createContent(this.content)));

    return new SendEmailRequest()
        .withSource(FROM_EMAIL)
        .withDestination(destination)
        .withMessage(message);
  }

  private Content createContent(final String text) {
    return new Content()
        .withCharset("UTF-8")
        .withData(text);
  }
}

이메일 전송용 DTO를 만들어줍니다. 외부에서는 생성자를 통해 받는 사람 리스트, 제목, 내용 입력 받고, 이런 내용들을 조합해서 amazonaws service를 통해 메일을 보낼 때 필요한 정보가 담겨있는 객체인 SendEmailRequest를 반환해줍니다.

 

6. 이메일 전송용 서비스

이제 전송용 DTO가 만들어졌으니, 서비스를 만들어보도록 하겠습니다.

@Slf4j
@Service
@RequiredArgsConstructor
public class SendEmailService {

    private final AmazonSimpleEmailService amazonSimpleEmailService;

    private void send(final String subject, final String content, final List<String> receivers) {
        final EmailSenderDto senderDto = EmailSenderDto.builder() // 1
          .to(receivers)
          .subject(subject)
          .content(content)
          .build();

        final SendEmailResult sendEmailResult = amazonSimpleEmailService // 2
            .sendEmail(senderDto.toSendRequestDto());

        sendingResultMustSuccess(sendEmailResult); // 3
    }

  private void sendingResultMustSuccess(final SendEmailResult sendEmailResult) {
    if (sendEmailResult.getSdkHttpMetadata().getHttpStatusCode() != 200) {
      log.error("{}", sendEmailResult.getSdkResponseMetadata().toString());
    }
  }

}
  1. 전달 받은 제목, 내용, 받는 사람을 생성자를 통해 EmailSenderDto를 만들어줍니다.
  2. AmazonSimpleEmailService에서 제공해주는 sendEmail()을 호출하며 메일 발송 시도를 합니다.
  3. 만약 실패했을 경우에는 로그를 찍어두게 처리했습니다. 예제이기 때문에 로그를 찍게 처리했는데, 예외를 던져도 무방할 것 같습니다! SendEmailResultgetSdkResponseMetadata()를 호출하면 실패 이유를 전달 받을 수 있습니다.

테스트를 돌려 확인해보니, 제대로 메일이 간 것을 확인할 수 있었습니다.


✨ 정리

SES를 이번에 처음 사용해보게 되어서 정리해보았습니다! 원래 계획은 SES의 이메일 템플릿 기능을 사용하는 것 까지 다루고 싶었는데, 생산성이 오히려 떨어지는 기묘한 경험을 하게 되어 해당 내용은 다루지 않았습니다. 😢

혹시 글을 읽으면서 잘못된 내용이 있으면 댓글로 알려주시면 감사하겠습니다! 읽어주셔서 감사합니다! 😊


👏 참고