728x90
반응형
간단한 Email 인증 시스템을 만들어보겠습니다.
동작 순서
- 사용자가 이메일을 입력
- 랜덤 코드를 생성
- Email 전송 라이브러리를 이용하여 코드를 사용자 Email에 전송
- 사용자가 인증 코드를 입력하면 인증 성공 (시스템 내부에서 맞는지 체크)
Dependencies 추가 [참고로 스프링 3.0 기준입니다.]
dependencies {
// 이메일 인증 관련 의존성
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '2.6.3'
implementation group: 'com.sun.mail', name: 'jakarta.mail', version: '2.0.1'
implementation group: 'com.sun.activation', name: 'jakarta.activation', version: '2.0.1'
// html 렌더링을 위한 템플릿
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
디렉터리 구조
공통으로 사용하는 global 패키지 내부에 email 패키지를 생성하여 구현하였습니다.
이메일 인증에 사용할 이메일 설정
저는 구글 이메일을 이용해서 인증 시스템을 구현해보겠습니다.
구글 계정 설정 → 보안 → 2단계 인증을 사용으로 변경합니다.
앱 선택은 '메일'로 해주시고, 기기 선택은 'Windows 컴퓨터'로 하고 생성을 클릭합니다.
다음과 같이 16자리 비밀번호가 생성되었다면 16자리 비밀번호를 메모장에 저장해둡니다.
다음은 Gmail 앱 설정 탭에서 다음과 같이 설정해줍니다.
구현
application.properties
#Email Auth Setting
spring.mail.host=smtp.gmail.com #네이버로 할 시 변경 smtp.naver.com
spring.mail.port=587
spring.mail.username={사용자 Email}
spring.mail.password={발급받은 비밀번호 16자리}
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.auth=true
EmailService.java
import hooyn.base.global.exception.CustomException;
import hooyn.base.global.exception.ErrorCode;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.util.Random;
@Service
@RequiredArgsConstructor
public class EmailService {
private final JavaMailSender emailSender;
private final TemplateEngine templateEngine;
//application.properties에서 사용자 Email 정보 가져오기
@Value("${spring.mail.username}")
private String from;
//이메일 전송이 오래걸려서 비동기로 처리하기 위해 @Async 키워드를 사용하여 처리하였습니다.
//다음과 같이 사용하기 위해서는 Application.class 에 @EnableAsync 을 추가해주어야 합니다.
@Async
public void sendAuthEmail(String email, String authCode) {
try {
MimeMessage emailForm = makeEmailForm(email, authCode);
emailSender.send(emailForm);
} catch (Exception e) {
throw new CustomException(ErrorCode.BAD_REQUEST, e.getMessage());
}
}
//이메일 인증을 위해 사용자에게 보낼 이메일 폼을 생성합니다.
private MimeMessage makeEmailForm(String email, String authCode) throws MessagingException {
MimeMessage message = emailSender.createMimeMessage();
message.addRecipients(MimeMessage.RecipientType.TO, email); //보낼 사람 설정
message.setSubject("LakeLight 회원가입 이메일 인증"); //이메일 제목
message.setFrom(from); //보내는 사람 설정
message.setContent(setContext(authCode), "text/html;charset=euc-kr"); //setContext를 통해 html을 만들고 전송
return message;
}
//HTML 제작하고 인증 코드도 HTML에서 보여줄 수 있도록 전달
private String setContext(String authCode) {
Context context = new Context();
context.setVariable("code", authCode);
return templateEngine.process("email", context);
}
//인증 코드 제작하는 코드
//비동기 처리를 위해 public으로 하고 Controller 부분에서 파라미터로 전달
public String makeAuthCode() {
Random random = new Random();
StringBuffer key = new StringBuffer();
for (int i = 0; i < 8; i++) {
int index = random.nextInt(3);
switch (index) {
case 0 -> key.append((char) (random.nextInt(26) + 97));
case 1 -> key.append((char) (random.nextInt(26) + 65));
case 2 -> key.append(random.nextInt(9));
}
}
return key.toString();
}
}
EmailContorller.java
import hooyn.base.global.response.ResponseWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/email/auth")
public class EmailController {
private final HttpServletRequest request;
private final EmailService emailService;
@PostMapping("")
public ResponseEntity<?> confirmEmailAuth(@RequestBody ConfirmEmailAuthRequestDto dto) {
//빠른 응답을 하고 로직은 비동기처리를 하기 위해 인증 코드를 생성하고,
String authCode = emailService.makeAuthCode();
//비동기로 처리하기 위해 인증 코드를 전달한다.
emailService.sendAuthEmail(dto.getEmail(), authCode);
return new ResponseEntity<>(new ResponseWrapper(request, HttpStatus.OK,
true, "이메일이 성공적으로 전송되었습니다.", "AuthCode: [" + authCode + "]" ), HttpStatus.OK);
}
}
ConfirmEmailAuthRequestDto.java
import lombok.Getter;
@Getter
public class ConfirmEmailAuthRequestDto {
private String email;
}
index.html 위치와 코드
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div style="margin:100px;">
<h1> 안녕하세요.</h1>
<h1> LakeLight Company 입니다.</h1>
<br>
<p> 아래 코드를 회원가입 창으로 돌아가 입력해주세요.</p>
<br>
<div style="font-family:verdana;">
<h3 style="color:blue"> 회원가입 인증 코드 입니다. </h3>
<div>
<div th:text="${code}"></div>
</div>
</div>
<br/>
</div>
</body>
</html>
Postman으로 API 호출 후 결과 화면
비동기로 처리하기 때문에 355ms 만 걸리는 것을 확인할 수 있습니다. 비동기로 처리하기 전에는 로컬 PC 기준 3초가 걸렸는데, 비동기로 처리하고 355ms 로 줄어들었습니다.
파라미터로 넘긴 email로 Email 온 화면
마무리
오늘은 늘 생각만으로 하던 이메일 인증에 대해서 구현해보았습니다.
라이브러리로 잘 구현되어 있어서
생각했던 것보다는 어려운 점이 없었던 것 같습니다.
다음 프로젝트를 할 때는 간단한게 적용해서 사용할 수 있을 것 같습니다.
또한 프로젝트에서 사용자에게 이메일을 보내야할 때 빠르게 구현할 수 있을 것 같습니다.
[참고]
728x90
반응형
'Spring' 카테고리의 다른 글
[Spring] Spring 기초 개념과 Spring MVC 동작원리 노트필기 (2) | 2022.12.01 |
---|---|
[Spring] Netty Server & Client 구현 (4) | 2022.11.23 |
[Spring] JPA Fetch Join (0) | 2022.09.05 |
[Spring] CGLIB: Code Generator Library (0) | 2022.08.24 |
[Spring] Server-Client 간 암, 복호화 구현 (0) | 2022.08.18 |