[Spring][smtp] Spring boot 이용한 메일발송
1. Google 메일 설정
spring boot를 이용하여 메일을 발송할 수 있습니다. 이번 글에서는 구글 메일을 통해 발송할것 입니다. 구글 메일을 이용하기 위해서는 다음과 같은 설정이 필요합니다.
- Google 홈페이지 > Google 계정 관리

- 보안 > 앱 비밀번호(이전에 2단계 인증설정이 필요)

- 메일, Windows 컴퓨터

- 앱 비밀번호 저장(smtp 설정에 사용될 예정)

- Gmail > 빠른 설정(우측상단 톱니바퀴) > 모든 설정 보기

- 전달 및 POP/IMAP(탭) > POP다운로드: 모든 메일에 POP사용하기 > IMAP 액세스: IMAP사용 > 변경사항 저장

2. Spring boot 에서 설정
gradle 에 추가
implementation 'org.springframework.boot:spring-boot-starter-mail'
SMTP 설정
application-mail.yml
아래와 같이 username, password 부분을 환경변수 처리하여 이용할 수 있습니다.
spring:
mail:
host: smtp.gmail.com
port: 587
username: ${MAIL_USERNAME} #SMTP용 google 계정(gmail 아이디)
password: ${MAIL_PASSWORD} # 앱 비밀번호
properties:
mail:
smtp:
starttls:
enable: true
required: true
auth: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
3. mail 발송
메일을 보내기 위해서는 수신자의 메일주소와 메일 제목, 내용이 필요합니다. 아래 코드에서 NotificationEmail 에 해당 내용들을 채워줍니다.
아래 로직은 controller 에서 수신자의 메일주소를 받고 AuthService에서 만들어진 NotificationEmail 을 바탕으로 MailService에서 실제로 메일을 보냅니다. 타임리프를 이용하여 미리 내용을 채워야 하는 메일 템플릿을 만들어 두고 해당 템플릿 파일의 이름과 채워야 하는 내용의 이름, 값으로 된 Map 을 통해 MailContentBuilder 에서 내용을 채웁니다. 여기서 주의할 점은 테스트 코드에서 mock처리를 고려하여 TemplateEngine 이 아닌 ITemplateEngine을 이용해야 합니다. (TemplateEngine 이용시 NPE 발생)
이후 MimeMessageHelper 를 통해 내용을 채웁니다. 이미지/업로드 전송을 위해서는 멀티파트 메시지 허용, html 허용 설정을 해줘야 합니다. 최종적으로 JavaMailSender의 send() 메소드로 메일을 전송합니다.
메일 전송을 할때는 비동기화 처리(@Async)를 하여 메일 전송이 끝날때까지 기다리지 않도록 합니다.
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class NotificationEmail {
private String receiver;
private String subject;
private String content;
public static NotificationEmail of(String receiver, String subject, String content) {
return new NotificationEmail(receiver, subject, content);
}
}
public class AuthService {
private final MailService mailService;
private final MailContentBuilder mailContentBuilder;
public String sendAuthenticationCodeEmail(final EmailRequest emailRequest) {
String code = AuthenticationCode.createCode().getCode();
String message = createAuthenticationCodeEmailContent(code);
NotificationEmail notificationEmail =
NotificationEmail.of(emailRequest.getEmail(), AUTH_CODE_MAIL_TITLE, message);
log.info("AuthService Thread: " + Thread.currentThread().getName());
mailService.sendMail(notificationEmail);
return code;
}
private String createAuthenticationCodeEmailContent(final String code) {
Map<String, String> contents = new HashMap<>();
contents.put(CODE_KEY, code);
return mailContentBuilder.build(AUTH_CODE_TEMPLATE, contents);
}
}
public class MailContentBuilder {
// TemplateEngine을 이용할 경우 @Mock 사용시 NPE 발생
private final ITemplateEngine templateEngine;
public String build(final String template, final Map<String, String> contents) {
Context context = new Context();
for (String key : contents.keySet()) {
context.setVariable(key, contents.get(key));
}
return templateEngine.process(template, context);
}
}
public class MailService {
private static final String ENCODING = "UTF-8";
private final JavaMailSender mailSender;
@Async
public void sendMail(final NotificationEmail notificationEmail) {
MimeMessagePreparator messagePreparator = toMimeMessagePreparator(notificationEmail);
try {
log.info("MailService Thread: " + Thread.currentThread().getName());
mailSender.send(messagePreparator);
} catch (MailException e) {
throw new CMailException();
}
}
private MimeMessagePreparator toMimeMessagePreparator(NotificationEmail notificationEmail) {
MimeMessagePreparator messagePreparator = mimeMessage -> {
// 이미지/업로드 전송을 위해서는 멀티파트 메시지 허용, html 허용 설정
// true는 멀티파트 메시지를 사용하겠다는 의미
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, ENCODING);
messageHelper.setTo(notificationEmail.getReceiver());
messageHelper.setSubject(notificationEmail.getSubject());
// true는 html 을 사용한다는 의미
messageHelper.setText(notificationEmail.getContent(), true);
};
return messagePreparator;
}
}
- MailSender
- 간단한 전자 메일을 보낼 수 있는 기능을 제공하는 최상위 인터페이스
- JavaMailSender
- MailSender를 상속받는 하위 인터페이스로 MIME 메시지를 지원하며 대부분 MimeMessage와 함께 사용합니다.
- JavaMailSenderImpl
- JavaMailSender 인터페이스를 구현한 클래스로 MimeMessage 및 SimpleMailMessage를 지원합니다.
- SimpleMailMessage
- 발신인, 수신인, 참조인, 제목 및 텍스트 필드를 포함한 간단한 메일 메시지를 작성하는데 사용됩니다.
- MimeMessagePreparator
- MIME 메시지를 준비하기 위한 콜백 인터페이스 제공합니다.
- MimeMessageHelper
- MIME 메시지를 만들기 위한 클래스로 HTML 레이아웃에서 이미지, 일반적인 메일 첨부 파일 및 텍스트 내용을 지원합니다.
**<참고>**참고>
MIME는 Multipurpose Internet Mail Extensions의 약자입니다. 기존 UUEncoding 방식은 ASCII(텍스트) 파일만 지원하여 음악, 워드 파일 등의 바이너리 파일을 전송할 수 없습니다. 이러한 방식을 보안하여 나온 인코딩 방식입니다.
<!DOCTYPE HTML>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>authentication code</title>
</head>
<body>
<div style='margin:100px;'>
<h1> 안녕하세요</h1>
<br>
<p>아래 코드를 회원가입 창으로 돌아가 입력해주세요</p>
<br>
<div align='center' style='border:1px solid black; font-family:verdana' ;>
<h3 style='color:blue;'>회원가입 인증 코드입니다.</h3>
<div style='font-size:130%'>
CODE : <strong><span th:text="${code}">인증코드</span></strong>
</div>
<br>
</div>
</div>
</body>
</html>
th:text=”${code} 의 code 에 내용이 채워지는 것으로 앞서 context.setVariable(key, contents.get(key)); 을 통해 내용을 채운것을 확인할 수 있습니다. (key: code, 값: contents.get(key))
reference
댓글남기기