⭐️ 목적
이메일 전송 기능은 인증코드를 통한 인증을 위해서 필요해졌다.
플랫폼 정책상 랜덤으로 생성된 인증 코드를 이메일로 전송하여 검증하는 방식을 채택했기 때문에
프로젝트에 이메일 전송 기능을 구현해야만 했다.
✅ 구현 과정 (간략)
1. 구글 메일 설정
이메일 전송에 사용할 SMTP 서버를 설정해야한다.
나의 경우 프로젝트 전용 구글 계정을 새로 생성하여 구글 SMTP 서버를 사용했다.
구글 계정을 통해 SMTP 서버를 사용하기 위해선 별도의 설정이 필요하다.
구글 메일 설정에 들어가서 다음과 같이 설정한다.
2. 구글 메일 연동
구글 보안 상 연동을 위해선 2단계 인증이 된 계정의 앱 비밀번호를 설정하여 사용해야 한다.
먼저 스프링부트 build.gradle에 메일 전송을 위한 의존성을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-mail'
스프링부트 application.yml에 앱 비밀번호로 Config를 추가한다.
spring:
mail:
host: smtp.gmail.com
port: 587 // 구글 SMTP 서버 기본 포트
username: 계정
password: 앱 비밀번호
properties:
mail:
smtp:
auth: true
timeout: 5000
starttls:
enable: true
위와 같이 작성할 경우 JavaMailSender 생성시 MailSenderAutoConfiguration로 인해 디폴트 값으로 설정된다.
2. 메일 전송
JavaMailSender를 이용할 경우 MimeMessage를 이용해 메일 내용 작성 후 전송한다.
(메시지 생성시 MimeMessageHelpler 이용)
수신자와 메일 내용을 지정하여 send 메소드를 이용하면 메일이 성공적으로 전송된다.
public String sendMail(String mail) {
String code = createCode();
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, false, "UTF-8");
mimeMessageHelper.setTo(mail);
mimeMessageHelper.setSubject("이메일 인증");
String body = "";
body += "<h3>" + "이메일 인증을 위한 인증코드입니다." + "</h3>";
body += "<div style='border: 2px solid black; padding: 10px; display: inline-block;'>" +
"<h1>" + code + "</h1>" +
"</div>";
body += "<h3>" + "6자리 코드를 정확히 입력해주세요!" + "</h3>";
mimeMessageHelper.setText(body, true);
javaMailSender.send(message);
} catch (MailException | MessagingException e) {
throw new BusinessException(ErrorCode.EMAIL_SEND_FAILURE);
}
return code;
}
실제 예시
✅ 고민한 부분
# 별거 아니긴 하지만 Mail Sender 인터페이스 선택
스프링부트에서 사용하는 Mail Sender 인터페이스에는 크게 두 가지가 있다.
- MailSender
- JavaMailSender (MailSender 확장)
이번 프로젝트에서 JavaMailSender를 이용했는데,
그 이유는 MimeMessage를 이용하기 때문이다.
대표적으로 스프링부트에서 사용하는 메시지 클래스로는 SimpleMailMessage, MimeMessage가 있는데,
MimeMessage는 텍스트뿐만 아니라 HTML 형식, 첨부 파일, 이미지 등의 복잡한 이메일을 보낼 수 있다.
서비스를 개발하기 때문에 메일을 이쁘게(?) 꾸밀 필요성이 있어서 MimeMessage를 이용하고자 했고,
그래서 JavaMailSender를 이용하게 되었다.
✅ 트러블 슈팅
# 앱 비밀번호 설정
처음에는 그냥 구글 계정 비밀번호로 설정했더니 메일이 보내지지 않았다.
그래서 검색을 해보니 앱 비밀번호가 필요하다 하여 이에 대해 알아보았다.
구글 공식 문서에는 다음과 같이 명시하고 있다.
앱 비밀번호란 보안 수준이 낮은 앱 또는 기기에 Google 계정에 대한 액세스 권한을 부여하는 16자리 비밀번호입니다. 앱 비밀번호는 2단계 인증이 사용 설정된 계정에서만 이용할 수 있습니다.
한 마디로, 스프링부트에 연동하게 될 경우 보안 수준이 낮아서
계정 비밀번호가 아닌 따로 앱 비밀번호를 설정해서 사용해야 한다는 것이다.
✅ 공부한 내용
# SMTP 서버란?
SMTP(Simple Mail Transfer Protocol)이란 인터넷을 통해 이메일 메시지를 보내고 받는 데 사용되는 통신 프로토콜을 말한다.
SMTP 서버는 발신 메일 서버라고 불리며, SMTP를 이용하여 메일을 전송(처리)하는 서버를 말한다. (오직 발신에 대해서만 관여한다.)
SMTP 작동 방식에 대해서도 알아보았다.
SMTP 서버는 SMTP 클라이언트로부터 이메일을 전달받아 수신자로 이메일을 전송한다. (전송 과정은 생략)
여기선 스프링부트 프로젝트가 SMTP 클라이언트, 구글 SMTP 서버가 SMTP 서버가 된다.
SMTP 클라이언트는 SMTP 서버와 연결을 시작하고,
수신자 세부 정보, 제목 및 본문이 포함된 이메일을 전송한다.
이후 SMTP 서버는 수신자 주소를 기반으로 적합한 다음 서버를 결정하여 전송한다.
# 구글 SMTP 보안 설정
위에서 언급한 application.yml에 작성한 SMTP 관련 설정 중 보안과 관련 된 것은 다음 부분이다.
smtp:
auth: true
timeout: 5000
starttls:
enable: true
auth: true 는 이메일 송신 시 서버에서 사용자 인증(계정과 앱 비밀번호를 사용)을 요구하게 하는 것이다.
starttls 는 TLS를 사용하여 이메일 송신 전에 평문 통신을 암호화된 통신으로 업그레이드하여 송신자의 정보가 노출되지 않도록 한다.
여기서 TLS란 뭘까?
SSL: SSL는 웹사이트와 브라우저 사이(또는 두 서버 사이)에 전송되는 데이터를 암호화하여 인터넷 연결을 보호하기 위한 표준 기술
TLS: TLS는 정보를 암호화해서 송수신하는 프로토콜로 SSL의 향상되고 안전한 버전
정리해보자면, SMTP는 TLS를 이용해서 메일 전송 시 보안을 유지한다.
# 궁금해서 JavaMailSender 알아보기
난 단순히 application.yml에서 spring.mail 아래에 값을 지정 했을 뿐인데,
JavaMailSender 생성 시 알아서 설정값(호스트, 유저, 비밀번호 등)이 설정 되는지 궁금해져서 알아봤다.
답은 메일을 보내기 위해 의존성을 추가한 모듈인 spring-boot-starter-mail 에 있었다.
MailSenderAutoConfiguration 클래스는 spring-boot-starter-mail 모듈에 포함 돼 있는 클래스다.
해당 클래스는 자동 구성 클래스로 spring.mail에 있는 속성을 읽고, 이를 사용하여 JavaMailSender를 설정한다.
@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class MailSenderAutoConfiguration {
@Bean
@ConditionalOnMissingBean(JavaMailSender.class)
public JavaMailSender javaMailSender(MailProperties properties) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(properties.getHost());
sender.setPort(properties.getPort());
sender.setUsername(properties.getUsername());
sender.setPassword(properties.getPassword());
sender.setJavaMailProperties(asProperties(properties.getProperties()));
return sender;
}
}
즉 어떤 Bean 객체에서 JavaMailSender 주입 시 내부적으로 알아서 매핑 된다는 소리다.
'스프링부트 메모' 카테고리의 다른 글
[스프링부트] Builder 패턴에 대해 알게 된 점 (0) | 2024.11.10 |
---|---|
[스프링부트] GenerationType.IDENTITY vs GenerationType.SEQUENCE (0) | 2024.11.07 |
[스프링부트] 자동 답변 봇 구현 (AI 서버와의 연결) (2) | 2024.09.25 |
[스프링부트] 디스코드 웹훅 연동하기 (1) | 2024.09.22 |
[스프링부트] 메모 시작 (0) | 2024.09.22 |