Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- break문
- function
- for문
- 메서드
- 컴파일
- 인텔리제이 한글 깨짐 해결법
- 시스템 환경 변수 편집
- 포함관계
- 메서드 오버로딩
- While
- JAVA객체지향
- 반복문
- this예약어
- 접근제어지시자
- Java
- 인텔리제이 기초 설정
- Thread
- 생성자
- Java데이터 타입
- OPP개념
- JAVA기초
- 형 변환
- 연관관계
- multi-threading
- java변수
- IntelliJ IDEA
- continue문
- 자바 멀티스레딩
- 상수
- 집합관계
Archives
- Today
- Total
최원종의 개발 블로그
V15-3 인증 이메일 발송 API 테스트 본문
인증 이메일 발송 API 테스트

MailUtil 코드
package com.tenco.blog._core.util;
import java.util.Random;
public class MailUtil {
// 6자리 랜덤 숫자 생성 (100000 ~ 999999)
public static String generateRandomCode() {
Random random = new Random();
int code = 100000 + random.nextInt(900000);
return String.valueOf(code);
}
}
UserRequest 코드
@Data
public static class EmailCheckDTO {
private String email; // 이메일 주소
private String code; // 인증번호(번호 확인시 사용(
public void validate() {
if(email == null || email.trim().isEmpty()) {
throw new Exception400("이메일을 입력해주세요");
}
if(!email.contains("@")) {
throw new Exception400("올바른 이메일 형식이 아닙니다");
}
}
}
MailService 코드
package com.tenco.blog.user;
import com.tenco.blog._core.util.MailUtil;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
/**
* 메일 발송 서비스
* - 비즈니스 로직
* 1. 인증 번호 생성
* 2. 이메일 발송
* 3. 세션에 인증번호 저장(검증용)
*
*/
@Service // IoC
@RequiredArgsConstructor
public class MailService {
// 스프링 부트가 제공하는 메일 발송 객체
// application-dev.yml 에 설정된 STMP 정보를 사용하여 메일 발송
private final JavaMailSender javaMailSender;
private final HttpSession session;
public void 인증번호발송(String email) {
// 1. 인증 번호 생성
String code = MailUtil.generateRandomCode();
System.out.println("생성된 인증 번호 : " + code);
// 2. 이메일 발송 (전송 내용 만들기)
// MimeMessage 란 - 이메일을 보낼 때 내용을 작성하는 편지봉투이다
// 텍스트 뿐만 아니라 HTML, 첨부파일등을 포함할 수 있는 포맷이다.
MimeMessage emailMessage = javaMailSender.createMimeMessage();
try {
// true 멀티파트 허용(파일 사용도 허용)
MimeMessageHelper helper = new MimeMessageHelper(emailMessage, true, "UTF-8");
helper.setTo(email); // 받는 사람 설정
helper.setSubject("[m-blog 회원가입 인증 번호]"); // 메일에 제목 설정
helper.setText("<h3>인증 번호는 "+ code +" 입니다</h3>", true); // 메일 본문 설정
javaMailSender.send(emailMessage);
// 세션에 인증번호 임시 저장
// 세션 --> devnote1@naver.com --> code_devnote1@naver.com : 123456
session.setAttribute("code_" + email, code);
System.out.println("인증번호 발송 완료 : " + code); // TODO - 추후 삭제
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
인증 번호 검증 API 테스트

UserApiController 코드
package com.tenco.blog.user;
// AJAX 통신 요청을 받아서 처리하는 컨트롤러
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController // Controller + Response
@RequiredArgsConstructor
public class UserApiController {
private final MailService mailService;
// 인증 번호 발송 API
// 주소 설계 : http://localhost:8080/api/email/send , POST
// @RequestBody <-- 클라이언트 단에서 application/json 형식으로 요청을 준다라고 약속
// 생성된 HTTP 요청 메세지에서 요청 본문(body) 값을 추출해서 객체로 변환 해
@PostMapping("/api/email/send")
public ResponseEntity<?> sendEmail(@RequestBody UserRequest.EmailCheckDTO reqDTO) {
reqDTO.validate();
mailService.인증번호발송(reqDTO.getEmail());
return ResponseEntity.ok("인증번호 발송 됨");
}
/**
* {
* "email" : "본인@naver.com",
* "code" : 123456
* }
*
*/
// 인증 번호 검증 요청 API
@PostMapping("/api/email/verify")
public ResponseEntity<?> 인증번호확인(@RequestBody UserRequest.EmailCheckDTO reqDTO) {
reqDTO.validate();
if(reqDTO.getCode() == null || reqDTO.getCode().trim().isEmpty()) {
return ResponseEntity.badRequest()
.body(Map.of("message", "인증번호를 입력해주세요"));
}
// 인증 번호 검사 로직 메일 서비스로 위임
boolean isVerified = mailService.인증번호확인(reqDTO.getEmail(), reqDTO.getCode());
// 결과에 따른 응답처리
if (isVerified) {
return ResponseEntity.ok()
.body(Map.of("message", "인증되었습니다"));
} else {
return ResponseEntity.badRequest()
.body(Map.of("message", "인증번호가 일치하지 않습니다"));
}
}
}
MailService 코드
package com.tenco.blog.user;
import com.tenco.blog._core.util.MailUtil;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
/**
* 메일 발송 서비스
* - 비즈니스 로직
* 1. 인증 번호 생성
* 2. 이메일 발송
* 3. 세션에 인증번호 저장(검증용)
*
*/
@Service // IoC
@RequiredArgsConstructor
public class MailService {
// 스프링 부트가 제공하는 메일 발송 객체
// application-dev.yml 에 설정된 STMP 정보를 사용하여 메일 발송
private final JavaMailSender javaMailSender;
private final HttpSession session;
public void 인증번호발송(String email) {
// 1. 인증 번호 생성
String code = MailUtil.generateRandomCode();
System.out.println("생성된 인증 번호 : " + code);
// 2. 이메일 발송 (전송 내용 만들기)
// MimeMessage 란 - 이메일을 보낼 때 내용을 작성하는 편지봉투이다
// 텍스트 뿐만 아니라 HTML, 첨부파일등을 포함할 수 있는 포맷이다.
MimeMessage emailMessage = javaMailSender.createMimeMessage();
try {
// true 멀티파트 허용(파일 사용도 허용)
MimeMessageHelper helper = new MimeMessageHelper(emailMessage, true, "UTF-8");
helper.setTo(email); // 받는 사람 설정
helper.setSubject("[m-blog 회원가입 인증 번호]"); // 메일에 제목 설정
helper.setText("<h3>인증 번호는 "+ code +" 입니다</h3>", true); // 메일 본문 설정
javaMailSender.send(emailMessage);
// 세션에 인증번호 임시 저장
// 세션 --> devnote1@naver.com --> code_devnote1@naver.com : 123456
session.setAttribute("code_" + email, code);
System.out.println("인증번호 발송 완료 : " + code); // TODO - 추후 삭제
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
public boolean 인증번호확인(String email, String code) {
// 1. 인증번호발송 API ---> 세션에 임시 코드값이 저장된 상태이다.
// code_devnote1@naver.com : 123456
String savedCode = (String) session.getAttribute("code_" + email); // 이 값으로 - value : 값을 추출
if (savedCode != null && savedCode.equals(code)) {
// 인증된 상태를 의미 한다
// 기존에 저장된 세션 값을 반드시 제거
session.removeAttribute("code_" + email);
// 이 이메일은 은증 완료됨이란 상태를 다시 세션에 저장한다 (도장찍어둔다)
// 회원 가입시 UserService 에서 이 도장을 확인하고 회원 가입 처리를 한다.
session.setAttribute("verified_email", email);
return true;
} else {
return false;
}
}
}
인증, 검증 UI + AJAX
\templates\user\join-form.mustache
{{> layout/header}}
<div class="container p-5 flex-grow-1">
<div class="card">
<div class="card-header"><b>회원가입을해주세요</b></div>
<div class="card-body">
<!--
중요!!
파일 업로드 사용시 method (POST)
enctype 을 반드시 multipart/form-data 설정 해주어야 한다.
설정을 안하면 파일 데이터를 서버측에서 받을 수 없음
(form 태그 사용시 서버측으로 파일 데이터를 보내는 방법)
-->
<form action="/join" method="post" enctype="multipart/form-data" onsubmit="return checkVerify()">
<div class="mb-3">
<input type="text" class="form-control" placeholder="enter username" name="username" value="cos5">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="enter password" name="password"
value="1234">
</div>
<div class="mb-3">
<label for="" class="form-lable">이메일</label>
<div class="input-group">
<input type="email" class="form-control email"
placeholder="enter email" name="email" value="aa@naver.com" required>
<button type="button" class="btn btn-outline-secondary" onclick="callSendApi()">인증번호 전송</button>
</div>
</div>
<!-- 인증번호 입력 (처음에 숨김, 발송 후 보임) -->
<div class="mb-3" id="code-box" style="display: none;">
<label for="" class="form-label">인증번호</label>
<div class="input-group">
<input type="text" class="form-control" id="code"
placeholder="인증번호 6자리" maxlength="6">
<button type="button" class="btn btn-outline-secondary"
onclick="callVerifyApi()">확인
</button>
</div>
<div id="msg"></div>
</div>
<!-- 인증 완료 여부 저장 필드 (hidden 필드) -->
<input type="hidden" id="isEmailVerified" value="false">
<!-- 프로필 이미지 등록 필드(선택 사항) -->
<div class="mb-3">
<label for="profileImage" class="form-label">프로필 사진(선택사항)</label>
<input type="file" id="profileImage" class="form-control" name="profileImage" accept="image/*">
<small class="form-text text-muted">이미지 파일만 업로드 가능합니다(JPG, PNG, GIF 등)</small>
<small class="form-text text-muted d-block mt-1">⭐ 프로필 사진을 등록하지 않아도 회원 가입 가능 </small>
</div>
<!-- submit 으로 다시 정정 -->
<button type="submit" class="btn btn-primary form-control" >회원가입</button>
</form>
</div>
</div>
</div>
<script>
// 1. 인증번호 메일 발송 API
async function callSendApi() {
const email = document.querySelector(".email").value;
if(email == false) {
alert("이메일을 입력해주세요");
return;
}
const response = await fetch("/api/email/send", {
method: "POST",
headers: {"Content-Type" : "application/json; charset=UTF-8"},
body: JSON.stringify({email : email}),
});
if(response.ok) {
alert("인증번호가 발송되었습니다. 메일함을 확인하세요");
// 인증 번호 입력창 노출
document.querySelector("#code-box").style.display = "block";
} else {
let error = await response.json();
alert("실패 : " + error.message);
}
} // end of callSendApi
// 2. 인증번호 검증 API
async function callVerifyApi() {
const email = document.querySelector(".email").value;
const code = document.querySelector("#code").value;
if(code == false || email == false) {
alert("이메일 또는 인증코드가 필요 합니다");
return;
}
try {
const response = await fetch("/api/email/verify",
{
method: "POST",
headers: {"Content-Type" : "application/json; charset=UTF-8"},
body: JSON.stringify({email : email, code: code})
});
const result = await response.json();
const msgElement = document.querySelector("#msg");
if(response.ok) {
console.log(result);
console.log(typeof result);
// 인증 성공
msgElement.innerHTML = `<span style='color:green'> ${result.message} </span>`;
document.querySelector("#isEmailVerified").value = true;
// 이메일 인증 성공 후 사용자가 값을 변경 못 하게 1단계 처리
document.querySelector(".email").readOnly = true;
} else {
msgElement.innerHTML = `<span style='color:red'> ${result.message} </span>`;
}
} catch(error) {
alert("인증실패" + error.message);
}
}
// 3. 회원가입 폼 제출 시 최종 체크
function checkVerify() {
let isVerified = document.querySelector("#isEmailVerified").value;
if(isVerified == "false") {
alert("이메일 인증을 먼저 완료해주세요");
return false; // 폼 제출 막기
}
return true; // 폼 제출 허용
}
</script>
{{> layout/footer}}'Spring boot 입문 > Spring boot 입문 -1' 카테고리의 다른 글
| V16-1 포인트 시스템(유료 게시글 기능 추가) (0) | 2026.06.23 |
|---|---|
| V15-4 이메일 인증 후 회원가입 로직 변경 (0) | 2026.06.23 |
| V15-2 이메일 인증 기능 추가 사전준비 및 프로그램 흐름 (0) | 2026.06.18 |
| V15-1 회원가입시 이메일 인증 처리 (0) | 2026.06.09 |
| V14 소셜로그인(카카오) 리팩토링 (0) | 2026.06.09 |
