개발지식 먹는 하마 님의 블로그
[내일배움캠프 73일차] Spring으로 인증코드 이메일 발송 구현 본문
Spring에서 이메일 발송하기
이메일 발송을 구현하기에 앞서 관련된 원리를 먼저 알아보았다.
📨이메일 프로토콜
이메일과 관련된 프로토콜에는 3가지가 있다.
그중 POP3와 IMAP 메일 수신용이고 SMTP는 발신용 프로토콜이다.
✅ SMTP 프로토콜 기반 메일 시스템
- Mail User Agent (MUA) : 이메일을 작성하거나 열람하는 클라이언트
- Mail Transfer Agent (MTA) : MUA로부터 메일을 전달받아서 외부로 전달, 받은 메일을 MDA로 메일을 전달
- Mail Delivery Agent (MDA) : 사용자의 메일함에 메일을 저장
이메일이 발송되는 과정은 다음과 같다.
MUA에서 메일을 작성하고 발송 요청을 보내면 이를 MTA가 실제로 발송한다.
발송된 메일은 메일을 받는 사람이 사용하는 email 도메인의 MTA로 전송되고
해당 MTA에서 MDA를 통해 최종적으로 메일함에 메일이 도착한다.
📨Spring Boot Mail
Spring에서 메일을 보낼 때는 거의 대부분 JavaMailSender를 사용한다.
JavaMailSender를 다음과 같이 설정하였다.
spring.mail.host=smtp.gmail.com
spring.mail.port=587
MUA로 JavaMailSender를, MTA로 Gmail-smtp 서버를 사용하는 것이다.
Config 설정
MailConfig는 다음과 같다.
package com.example.cs25.global.config;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
@Configuration
public class MailConfig {
@Value("${spring.mail.host}")
private String host;
@Value("${spring.mail.port}")
private int port;
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
@Value("${spring.mail.properties.mail.smtp.auth}")
private boolean auth;
@Value("${spring.mail.properties.mail.smtp.starttls.enable}")
private boolean starttlsEnable;
@Value("${spring.mail.properties.mail.smtp.starttls.required}")
private boolean starttlsRequired;
@Value("${spring.mail.default-encoding}")
private String defaultEncoding;
@Value("${spring.mail.properties.mail.smtp.connectiontimeout}")
private int connectionTimeout;
@Value("${spring.mail.properties.mail.smtp.timeout}")
private int timeout;
@Value("${spring.mail.properties.mail.smtp.writetimeout}")
private int writeTimeout;
@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(host);
mailSender.setPort(port);
mailSender.setUsername(username);
mailSender.setPassword(password);
mailSender.setDefaultEncoding(defaultEncoding);
mailSender.setJavaMailProperties(getMailProperties());
return mailSender;
}
private Properties getMailProperties() {
Properties properties = new Properties();
properties.put("mail.smtp.auth", auth);
properties.put("mail.smtp.starttls.enable", starttlsEnable);
properties.put("mail.smtp.starttls.required", starttlsRequired);
properties.put("mail.smtp.connectiontimeout", connectionTimeout);
properties.put("mail.smtp.timeout", timeout);
properties.put("mail.smtp.writetimeout", writeTimeout);
return properties;
}
}
인증코드 생성
인증코드를 메일로 발송하기 위해서 먼저 인증코드를 생성한다.
난수를 생성하는 알고리즘은 Math.Random이 친숙했으나
보안에 취약하다는 문제점이 있다고 하여 SecureRandom을 사용하였다.
자바의 난수생성기 Random, SecureRandom
이메일로 임시비밀번호를 전송하려고할때, 난수를 생성하여 전달해주려고하던찰나에 궁금증이생겼다.학부시절에 처음 난수를생성할때 Random함수를 가끔 썼던거같은데 이번에 SecureRandom 에대해
velog.io
private String create() {
int length = 6;
Random random;
try {
random = SecureRandom.getInstanceStrong();
} catch (
NoSuchAlgorithmException e) { //SecureRandom.getInstanceStrong()에서 사용하는 알고리즘을 JVM 에서 지원하지 않을 때
random = new SecureRandom();
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(random.nextInt(10));
}
return builder.toString();
}
📦 Redis에 저장 및 관리
발급된 인증코드는 Redis에서 관리한다.
Key - VERIFY:이메일 주소
Value - 인증코드
private static final String PREFIX = "VERIFY:";
private void save(String email, String code, Duration ttl) {
redisTemplate.opsForValue().set(PREFIX + email, code, ttl);
}
인증코드를 검증할 때, Redis에서 이메일에 따른 인증코드를 확인한 후 TTL이 만료되어 키 값이 없으면 예외를 반환하고
데이터가 있다면 입력된 값과 일치 여부를 확인한다.
📨Thymeleaf 사용한 Html 형식의 이메일 내용 구성
추후 메일로 문제풀이 링크를 보내야 했기 때문에 Html 형식으로 이메일 내용을 구성할 필요가 있었다.
Html 형식을 지원하는 MimeMessage 객체를 사용하였다.
String htmlContent = templateEngine.process("mail-template", context);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(subscription.getEmail());
helper.setSubject("[CS25] 오늘의 문제 도착");
helper.setText(htmlContent, true);
Thymeleaf은 다음과 같은 장점이 있어 메일 내용 구현에 적용하였다.
- Spring과 호환성이 높다.
- Context 메서드를 통해 쉽게 데이터 주입이 가능하다.
- 동적 렌더링이 가능하다.
Context context = new Context();
context.setVariable("toEmail", subscription.getEmail());
context.setVariable("question", quiz.getQuestion());
context.setVariable("quizLink", generateQuizLink(subscription.getId(), quiz.getId()));
📝 구현 결과
인증코드 발급 api를 호출했을 때, 오른쪽과 같이 Html 형식의 이메일이 정상적으로 발송되는 것을 확인할 수 있다.
인증코드의 일치 여부 역시 정상적으로 검증하는 것을 확인할 수 있다.
'내일배움캠프 (CS25)' 카테고리의 다른 글
[내일배움캠프 75일차] 메일 발송 MessageQueue 적용하기 (0) | 2025.06.10 |
---|---|
[내일배움캠프 74일차] 문제풀이 링크 발송하기 (0) | 2025.06.05 |
[내일배움캠프 72일차] 이메일 인증 코드를 Redis로 관리하는 이유 (0) | 2025.06.02 |
[내일배움캠프 71일차] 크롤링한 Json 데이터 DB 저장 (0) | 2025.05.30 |
[내일배움캠프 70일차] Java 기반 웹 크롤링 (1) | 2025.05.29 |