공부/프로그래밍

[aws,ses] SES(Simple Email Service)서비스로 메일전송하기(springboot)

demonic_ 2020. 8. 27. 08:00

AccessToken을 이용해 전송할 것이기 때문에 IAM에서 계정에 따른 KEY를 받아두어야 한다.

해당계정에 다음 권한이 포함되어 있어야 한다.

 

 

그럼 이제 연동을 시작하자

 

 

# application.properties에 등록 및 AWS SES에 Email 인증하기

application.properties에 다음항목을 추가하자.

# AWS SES(이메일) 액세스 Key
aws.ses.credentials.access-key: [accessKey]
aws.ses.credentials.secret-key: [secretKey]
# AWS SES 인증 이메일이자 전송시 보낸이의 이메일이어야 함
aws.ses.veritied.email=[이메일 주소]

 

전송할 이메일을 등록할 경우, 사전에 확인된 메일만 등록할 수 있는데, 이것은 AWS 콘솔에서 설정해야 한다.

 

1) AWS Console -> SES -> Email Addresses 메뉴로 이동

2) Verify a New Email Address 를 클릭한 후 메일전송

 

한가지 유의해야 할 점은 리전별로 확인을 해야한다는 점이다.

내 경우 서울에서 했기 때문에 서울리전(ap-northeast-2)에서 테스트 했다.

 

입력한 이메일을 확인하러 가보면 AWS로부터 인증요청 메일이 와 있다.

 

메일내용 중 도메인을 클릭해서 최종 승인을 마친 후 Email Addresses 메뉴로 이동하면 다음과 같이 Verified 표시가 되어있다.

 

인증이 완료된 메일주소로만 전송이 가능하니 참고하자.

 

그리고 이메일은 하루에 200개이상 보낼 수 없다. 이것을 늘리려면 AWS본사에 문의를 해야하는데, 왠만한 사유는 다 넘어가 준다. 내 경우 하루 50000개 까지 보낼수 되었다.(기본 시작단위가 5만 개 인듯.)

이건 상황에 따라 요청을 하면 될거 같다.

 

아래 항목은 AWS Console -> SES -> Sending Statistics 메뉴에서 확인이 가능하다.

 

그럼 본격 Spring boot와 연동해보자

 

 

# SES 연동 Config 생성

우선 depedency를 추가한다.

 

compile group: 'com.amazonaws', name: 'aws-java-sdk-ses', version: '1.11.847'

 

버전을 지정하지 않아도 다운이 받아진다면 버전을 삭제해도 상관없다.

 

연동 역할을 할 설정클래스를 생성하자

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * AWS SES(이메일) 서비스 Config
 */
@Configuration
public class AwsSesConfig {
    @Value("${aws.ses.credentials.access-key}")
    private String accessKey;

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

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

        return AmazonSimpleEmailServiceClientBuilder.standard()
                .withCredentials(awsStaticCredentialsProvider)
                .withRegion("ap-northeast-2") // 인증받은 리전에서 수행해야한다. 등록한 리전이 endpoint 가 됨 => https://email.AP_NORTHEAST_2.amazonaws.com,
                .build();
    }
}

 

내용을 보면 BasicAWSCredentials를 통해 accessKey와 secretKey를 입력하여 인증받는다.

이것을 이용해 AmazonSimpleEmailServiceClientBuilder를 구현하여 Bean으로 등록했는데, 이때 Region을 메일 인증한 Region과 동일해야 한다는 점이다.

참고로 등록한 리전을 통해서 SMTP 엔드포인트를 정하는거 같다. 오타(또는 대소문자 구분)가 나면 다음과 같은 에러를 리턴한다.

com.amazonaws.services.simpleemail.model.AmazonSimpleEmailServiceException: Credential should be scoped to a valid region, not 'AP-NORTHEAST-2'. (Service: AmazonSimpleEmailService; Status Code: 403; Error Code: SignatureDoesNotMatch; Request ID: 0d39e8fc-2448-430a-91dc-ddae7e99d1b7; Proxy: null)

인증된 메일이 없는 Region을 선택하여 시도하면 다음과 같은 에러를 발생한다.

com.amazonaws.services.simpleemail.model.MessageRejectedException: Email address is not verified. The following identities failed the check in region US-WEST-2: varkiry05@naver.com (Service: AmazonSimpleEmailService; Status Code: 400; Error Code: MessageRejected; Request ID: 7194f931-8564-4499-93fd-abd6b730f59e; Proxy: null)

 

 

이제 전송하기 위한 DTO를 만들자.

 

이부분은 창천항로님의 블로그를 참조하여 만들었다.

https://jojoldu.tistory.com/246

 

AWS SES (Simple Email Service) Spring Boot 프로젝트에서 사용하기

안녕하세요? 이번시간에는 AWS의 SES를 Java로 사용해보는 과정을 진행해보려 합니다. 개인 프로젝트 중 email 발송기능이 필요했는데, 생각보다 Java로 AWS SES를 사용하는 방법이 잘 공유되어 있지 않

jojoldu.tistory.com

 

import com.amazonaws.services.simpleemail.model.*;
import lombok.Builder;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;

@Getter
public class SenderDto {
    private String from;
    private List<String> to = new ArrayList<>();
    private String subject;
    private String content;

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

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

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

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

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

 

마지막으로 전송할 객체를 만들어 주자

import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.model.SendEmailResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

@Slf4j
@Component
@RequiredArgsConstructor
public class EmailSender {

    private final AmazonSimpleEmailService amazonSimpleEmailService;

    @Value("${aws.ses.veritied.email}")
    private String from;

    /**
     * 이메일 전송
     */
    public void send(String subject, String content, List<String> receivers) {
        if(receivers.size() == 0) {
            log.error("메일을 전송할 대상이 없습니다: [{}]", subject);
            return;
        }

        SenderDto senderDto = SenderDto.builder()
                .from(from)
                .to(receivers)
                .subject(subject)
                .content(content)
                .build();

        SendEmailResult sendEmailResult = amazonSimpleEmailService.sendEmail(senderDto.toSendRequestDto());

        if(sendEmailResult.getSdkHttpMetadata().getHttpStatusCode() == 200) {
            log.info("[AWS SES] 메일전송완료 => " + senderDto.getTo());
        }else {
            log.error("[AWS SES] 메일전송 중 에러가 발생했습니다: {}", sendEmailResult.getSdkResponseMetadata().toString());
            log.error("발송실패 대상자: " + senderDto.getTo() + " / subject: " + senderDto.getSubject());
        }
    }
}

 

받는 이, 제목, 내용은 외부에서 파라미터로 받게 하였고, 보내는 사람은 properties에 등록된 메일을 가져오게 했다. 보내는 email은 정해져 있기 때문에 지정하는게 좋다고 판단했다.

 

마지막으로 이메일 전송성공 여부를 판단할 파라미터가 마땅치 않다.

그래서 내경우는 통신상태를 확인하는 status를 기준으로 판단하였다.

 

이제 전송테스트를 해보자.

 

 

 

# 테스트 작성

테스트는 다음과 같이 작성한다

@RunWith(SpringRunner.class)
@SpringBootTest
class EmailSenderTest {
    @Autowired
    private EmailSender sesEmailSender;

    @Test
    @DisplayName("AWS SES 이메일전송 테스트")
    void sesSendTest() {
        // given

        ArrayList<String> to = new ArrayList<>();
        to.add("varkiry05@naver.com");
        String subject = "[AWS SES] 테스트발송";
        String content = "SES 메일발송 테스트입니다";

        // when
        sesEmailSender.send(subject, content, to);

        // then
    }
}

 

테스트를 실행해보면 다음의 값을 확인할 수 있다.

 

메일을 열람해보면 테스트 메일이 전송되어 있다.

 

끝.

 

 

 

참조:

https://jojoldu.tistory.com/246

 

AWS SES (Simple Email Service) Spring Boot 프로젝트에서 사용하기

안녕하세요? 이번시간에는 AWS의 SES를 Java로 사용해보는 과정을 진행해보려 합니다. 개인 프로젝트 중 email 발송기능이 필요했는데, 생각보다 Java로 AWS SES를 사용하는 방법이 잘 공유되어 있지 않

jojoldu.tistory.com

 

https://docs.aws.amazon.com/ko_kr/ses/latest/DeveloperGuide/smtp-connect.html

 

Amazon SES SMTP 엔드포인트에 연결 - Amazon Simple Email Service

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

https://djunnni.gitbook.io/springboot/2019-12-01

 

11. SpringBoot에서 Amazon SES 서비스 사용하기

 

djunnni.gitbook.io