오늘은 Spring 기반으로 회원가입 시 이메일 인증 코드를 발급해 주는 기능을 구현해보려고 합니다.
해당 기능을 구현함으로써 보안성을 강화하고, 서비스의 신뢰성을 높일뿐더러, 스팸 및 악성 가입을 방지할 수 있습니다. 또한 Spring에서는 JavaMail 모듈이 워낙 잘 되어 있어서 해당 모듈을 활용해야겠다고 생각했습니다.
SMTP를 이용하여 메일 서버를 구축하고, 해당 프로젝트 내에서 MailConfig, mail.properties 설정을 통해 구현하는 과정에 대해 상세히 설명드리도록 하겠습니다.
SMTP (Simple Mail Transfer Protocol)
- 인터넷을 통해 이메일을 보내고 받는 데 사용되는 통신 프로토콜
Spring Mail Module
- JavaMail API를 추상화하여 편리한 메일 전송 기능을 제공합니다.
- 해당 API에서 제공하는 MimeMessage를 이용하여 전달한 이메일의 내용을 구성할 수 있습니다.
- 파일과 같은 내용을 첨가하려면 MimeMessageHelper 클래스를 이용해야 합니다.
Dependency
implementation 'org.springframework.boot:spring-boot-starter-mail'
mail.properties
spring.mail.host=smtp.naver.com
spring.mail.username={username}
spring.mail.password={password}
MailConfig
@PropertySource("classpath:mail.properties")
@Configuration
public class MailConfig {
private static final int SMTP_SERVER_PORT_NUMBER = 465;
@Value("${spring.mail.host}")
private String host;
@Value("${spring.mail.username}")
private String id;
@Value("${spring.mail.password}")
private String password;
@Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(host); // 메인 도메인 서버 주소 => 정확히는 smtp 서버 주소
javaMailSender.setUsername(id); // 네이버 아이디
javaMailSender.setPassword(password); // 네이버 비밀번호
javaMailSender.setPort(SMTP_SERVER_PORT_NUMBER); // 메일 인증서버 포트
javaMailSender.setJavaMailProperties(getMailProperties()); // 메일 인증서버 정보 가져오기
return javaMailSender;
}
private Properties getMailProperties() {
Properties properties = new Properties();
properties.setProperty("mail.transport.protocol", "smtp"); // 프로토콜 설정
properties.setProperty("mail.smtp.auth", "true"); // smtp 인증
properties.setProperty("mail.smtp.starttls.enable", "true"); // smtp strattles 사용
properties.setProperty("mail.debug", "true"); // 디버그 사용
properties.setProperty("mail.smtp.ssl.trust", "smtp.naver.com"); // ssl 인증 서버는 smtp.naver.com
properties.setProperty("mail.smtp.ssl.enable", "true"); // ssl 사용
return properties;
}
}
이제 스프링 애플리케이션 단에서 메일 서버 설정을 마쳤습니다.
다음으로 이메일 인증코드 발급 로직에 대해 작성해 보도록 하겠습니다.
MailService
@RequiredArgsConstructor
@Service
public class EmailService {
private static final int CERTIFICATION_NUMBER_LENGTH = 8;
private static final int CERTIFICATION_NUMBER_OPTION = 3;
private static final int NUM_LETTERS_ALPHABET = 26;
private static final int ASCII_OFFSET_LOWER_A = 97;
private static final int ASCII_OFFSET_UPPER_A = 65;
private static final int NUM_DIGITS = 10;
private String ePw;
private final EmailAuthRepository emailAuthRepository;
private final JavaMailSender emailSender;
public MimeMessage createMessage(String to) throws MessagingException, UnsupportedEncodingException {
MimeMessage message = emailSender.createMimeMessage();
message.addRecipients(RecipientType.TO, to);// to => 보내는 대상
message.setSubject("Hand-Over 회원가입 이메일 인증");// 메일 제목
// 메일 내용
// 아래에서 메일의 subtype 을 html 로 지정해주었기 때문인지 html 문법을 사용가능하다
String msgg = "";
msgg += "<div style='margin:100px;'>";
msgg += "<h1> 안녕하세요. 케어 서비스를 제공해주는 매칭 플랫폼 Hand-Over 입니다.</h1>";
msgg += "<h1 style='color:green;'>인증번호 안내 메일입니다.</h1>";
msgg += "<br>";
msgg += "<p>" + to + "님 Hand-Over 플랫폼의 회원가입을 환영합니다.<p>";
msgg += "<br>";
msgg += "<p>해당 이메일은 회원가입을 위한 인증번호 안내 메일입니다.<p>";
msgg += "<br>";
msgg += "<p>하단 인증번호를 '이메일 인증번호' 칸에 입력하여 가입을 완료해주세요.<p>";
msgg += "<br>";
msgg += "<div align='center' style='border:1px solid black; font-family:verdana';>";
msgg += "<h3 style='color:blue;'>회원가입 인증 코드입니다.</h3>";
msgg += "<div style='font-size:130%'>";
msgg += "CODE : <strong>";
msgg += ePw + "</strong><div><br/> "; // 인증번호 넣기
msgg += "</div>";
message.setText(msgg, "utf-8", "html");// 내용, charset 타입, subtype
message.setFrom(new InternetAddress("handover1@naver.com", "핸드오버 관리자"));
return message;
}
public String createKey() {
StringBuffer key = new StringBuffer();
Random random = new Random();
for (int i = 0; i < CERTIFICATION_NUMBER_LENGTH; i++) { // 인증코드 8자리
int index = random.nextInt(CERTIFICATION_NUMBER_OPTION); // 0~2 까지 랜덤, rnd 값에 따라서 아래 switch 문이 실행됨
switch (index) {
case 0:
key.append((char) ((int) (random.nextInt(NUM_LETTERS_ALPHABET)) + ASCII_OFFSET_LOWER_A)); // 영어 소문자
// a~z (ex. 1+97=98 => (char)98 = 'b')
break;
case 1:
key.append((char) ((int) (random.nextInt(NUM_LETTERS_ALPHABET)) + ASCII_OFFSET_UPPER_A)); // 영어 대문자
// A~Z
break;
case 2:
key.append((random.nextInt(NUM_DIGITS))); // 숫자
// 0~9
break;
}
}
return key.toString();
}
@Transactional
public String sendSimpleMessage(String to) throws Exception {
ePw = createKey();
MimeMessage message = createMessage(to);
try {
if(emailAuthRepository.existsByEmail(to)) {
emailAuthRepository.deleteByEmail(to);
}
emailSender.send(message); // 메일 발송
} catch (MailException errorMessage) {
errorMessage.printStackTrace(); // 에러의 발생근원지를 찾아서 에러 출력
throw new IllegalArgumentException();
}
EmailAuth emailAuth = new EmailAuth(ePw, to);
emailAuthRepository.save(emailAuth);
return ePw; // 메일로 보냈던 인증 코드를 서버로 반환
}
}
위에서 설명드렸듯이 MimeMessage를 통해 클라이언트에게 전송할 메시지를 생성하고, JavaMailSender 클래스를 활용하여 랜덤으로 생성한 인증코드, 메시지를 전달할 수 있게 되었습니다.
이상으로 포스팅 마치겠습니다. 감사합니다.
'Java > Spring' 카테고리의 다른 글
[Spring] Swagger3.0 버전 Authorize button 설정 (1) | 2023.05.12 |
---|---|
[Spring] LocalDateTime 형식의 데이터를 @JsonFormat, @DateTimeFormat어노테이션을 적용하여 데이터 주고받기 (0) | 2023.05.10 |
[Spring] N+1 문제를 해결하기 위한 방안 (fetch join, @EntityGraph) (0) | 2023.03.18 |
[Spring] Spring Data JPA Paging, Sort 기능 (0) | 2023.03.18 |
[Spring] Spring Data JPA 공통 인터페이스 (0) | 2023.03.18 |