오늘은 프로젝트에서 구현한 이메일 인증에 대해서 작성하려한다.
Gradle을 사용하여 구현을 시작한다. 기준은 네이버메일이다.
Build.gradle에서 스프링 메일과 버전에 맞게 jakarta.mail을 불러온다.
// spring mail
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail
implementation 'org.springframework.boot:spring-boot-starter-mail:3.1.1'
// https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail
implementation group: 'com.sun.mail', name: 'jakarta.mail', version: '2.0.1'
현재 기준으로 스프링부트 메일 최신버전은 3.1.1이고 jakarta.mail 최신버전은 2.0.1이다.
그리고 application.properties에서 SMTP 코드를 추가한다.
spring.mail.host=smtp.naver.com
spring.mail.port=465
spring.mail.username= 본인 이메일주소
spring.mail.password= 본인 비밀번호
spring.mail.properties.debug=true
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable= true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.ssl.trust=smtp.naver.com
그리고 네이버 메일로 가서 pop3,smtp설정을 들어간다.

POP3/SMTP 사용 : 사용함으로 변경하고 나머지 설정은 그대로 둬도 상관없다. 그리고 저장하고 나오면 네이버 메일 준비는 끝이다.
그리고 config 패키지 추가후 EmailConfig 클래스를 추가한다.
@Configuration
@PropertySource("classpath:application.properties")
public class EmailConfig {
@Value("${spring.mail.username}")
private String id;
@Value("${spring.mail.password}")
private String password;
@Value("${spring.mail.host}")
private String host;
@Value("${spring.mail.port}")
private int port;
@Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(host); // smtp 서버 주소
javaMailSender.setUsername(id); // 설정(발신) 메일 아이디
javaMailSender.setPassword(password); // 설정(발신) 메일 패스워드
javaMailSender.setPort(port); //smtp port
javaMailSender.setJavaMailProperties(getMailProperties()); // 메일 인증서버 정보 가져온다.
javaMailSender.setDefaultEncoding("UTF-8");
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 starttls 사용
properties.setProperty("mail.debug", "true"); // 디버그 사용
properties.setProperty("mail.smtp.ssl.trust","smtp.naver.com"); // ssl 인증 서버 주소
properties.setProperty("mail.smtp.ssl.enable","true"); // ssl 사용
return properties;
}
}
이코드들은 application.properties에서 작성한 코드들에서 PropertySource 어노테이션을 통해 가져온 데이터들을 객체에 담아 준다. 그리고 javaMailService에 빈을 등록한후 javaMailSender에 정보들을 담아준후 초기화를 시켜준다. 그후 getMailProperties가 Getter역할을 한다.
그후 MailController 클래스를 생성한다.
@Slf4j
@RequiredArgsConstructor
@Controller
@RequestMapping("/user")
public class UserController {
private final EmailService emailService;
// 이메일 인증
@PostMapping("/signup/mailConfirm")
@ResponseBody
public String mailConfirm(@RequestParam String email) throws Exception {
String code = emailService.sendSimpleMessage(email);
log.info("인증코드 : " + code);
return code;
}
@PostMapping("/signup/checkCode")
@ResponseBody
public String checkCode(@RequestParam String code) {
if (code.equals(emailService.getEPw())) {
return "success";
} else {
return "failure";
}
}
}
메일을 보내기위해서는 무조건 ResponseBody 어노테이션을 추가해야 프론트엔드에서 그 정보를 받아갈수있다.
mailConfirm은 메일을 보내주는 역할을 하며 후에 만들 EmailService에 sendSimpleMessge를 받아오고 파라미터로 email을 받아 그것을 code에 담아주고 그것을 초기화시켜 이메일을 보내주는 역할을 한다.
checkCode는 프론트엔드에서 회원가입시 이메일 주소를 입력하고 인증코드를 입력했을때 그것을 확인하고 조건문을 통하여 code에서 equals로 emailService.getEPw 메서드를 통해 그것과 검증을 통하여 맞으면 success를 반환하고 아니면 failure을 반환 시켜준다.
그후 마지막으로 EmailService 클래스를 생성한다.
@PropertySource("classpath:application.properties")
@Slf4j
@RequiredArgsConstructor
@Service
public class EmailService {
private final JavaMailSender javaMailSender;
//인증번호 생성
private String ePw;
@Value("${spring.mail.username}")
private String id;
public MimeMessage createMessage(String to)throws MessagingException, UnsupportedEncodingException {
log.info("보내는 대상 : "+ to);
log.info("인증 번호 : " + ePw);
MimeMessage message = javaMailSender.createMimeMessage();
message.addRecipients(MimeMessage.RecipientType.TO, to); // to 보내는 대상
message.setSubject("자취생들을 위한 레시피 회원가입 인증 코드"); //메일 제목
// 메일 내용 메일의 subtype을 html로 지정하여 html문법 사용 가능
String msg="";
msg += "<div style='margin:100px;'>";
msg += "<h1> 안녕하세요</h1>";
msg += "<h1> 자취생들을 위한 레시피 입니다</h1>";
msg += "<br>";
msg += "<p>아래 코드를 회원가입 창으로 돌아가 입력해주세요<p>";
msg += "<br>";
msg += "<p>항상 당신의 레시피를 응원합니다. 감사합니다!<p>";
msg += "<br>";
msg += "<div align='center' style='border:1px solid black; font-family:verdana';>";
msg += "<h3 style='color:blue;'>회원가입 인증 코드입니다.</h3>";
msg += "<div style='font-size:130%'>";
msg += "CODE : <strong>";
msg += ePw + "</strong><div><br/> "; // 메일에 인증번호 넣기
msg += "</div>";
message.setText(msg, "utf-8", "html"); //내용, charset타입, subtype
message.setFrom(new InternetAddress(id,"Tean8Recipe")); //보내는 사람의 메일 주소, 보내는 사람 이름
return message;
}
// 인증코드 만들기
public static String createKey() {
StringBuffer key = new StringBuffer();
Random rnd = new Random();
for (int i = 0; i < 8; i++) { // 인증코드 8자리
int index = rnd.nextInt(3); // 0~2 까지 랜덤, rnd 값에 따라서 아래 switch 문이 실행됨
switch (index) {
case 0:
key.append((char) ((int) (rnd.nextInt(26)) + 97));
// a~z (ex. 1+97=98 => (char)98 = 'b')
break;
case 1:
key.append((char) ((int) (rnd.nextInt(26)) + 65));
// A~Z
break;
case 2:
key.append((rnd.nextInt(10)));
// 0~9
break;
}
}
return key.toString();
}
/*
메일 발송
sendSimpleMessage의 매개변수로 들어온 to는 인증번호를 받을 메일주소
MimeMessage 객체 안에 내가 전송할 메일의 내용을 담아준다.
bean으로 등록해둔 javaMailSender 객체를 사용하여 이메일 send
*/
public String sendSimpleMessage(String to)throws Exception {
ePw = createKey();
MimeMessage message = createMessage(to);
try{
javaMailSender.send(message); // 메일 발송
}catch(MailException es){
es.printStackTrace();
throw new IllegalArgumentException();
}
return ePw; // 메일로 보냈던 인증 코드를 서버로 리턴
}
public String getEPw() {
return ePw;
}
}
똑같이 데이터를 받아오기위해 @PropertySource("classpath:application.properties") 어노테이션을 추가한다.
private String ePw;라는 객체를 추가하여 여기에 인증코드를 담아줄것이다.
그리고 private String id위에 밸류가 있는데 EmailConfig랑 같은 역할이다 여기에 어플리케이션프로퍼티에 적은 이메일주소가 저 객체에 담겨진다.
그리고 createMessage라는 메서드에 html문법이 있는데 이것은 메일 내용에 html문법이 가능하여 사용 할 수 있다.
그리고 message.setFrom에 파라미터로 id, prac_Admin이 있는데 id는 보내는 사람의 주소가 담겨져야 오류없이 사용이가능하고 prac_Admin은 언제든지 변경할 수 있다 보내는 사람의 이름이 바뀌는 역할이다.
그리고 createKey 메서드로 넘어오면 random과 stringbuffer가 있는데 random은 인증코드를 8자리를 만들것인데 그것을 랜덤하게 섞어주는 메서드이다. stringBuffer는 반복문 for문을 8번 돌리고 그안에 스위치문으로 랜덤한 문자와 숫자들을 섞어줘서 그것을 key에 담아주는데 그 키가 int로 반환이 될것이다. 그러면 인증코드를 불러올때 오류가 발생할 수 있다. 그것을 String 타입으로 다시 바꿔주는 메서드이며 마지막에 return key.toString()이 있는데 같은 역할을 해주는 메서드이다.
그리고 메일 발송을 해주는 메서드인 sendSimpleMessage는 랜덤한 값을 뽑아준 createKey를 ePw 객체에 받아주고 그것을 통해 예외처리를 통하여 문제가 없을시 send를 통하여 메일 발송을 해준다. 그리고 그값을 서버로 초기화를 시켜준다.
getEPw는 서버로 초기화 시킨 값을 getter역할로 담아준다.
서버코드는 이것으로 마무리다.
클라이언트 코드는 이렇다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"/>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
<style>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}
.top {
width: 100%;
height: 10%;
border: 1px solid black;
}
.mid {
width: 80%;
height: 70%;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="top">
회원가입
</div>
<div class="mid">
<form action="/user/signup" method="post">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">userId</span>
<input type="text" class="form-control" name="userId" placeholder="Id" aria-label="Username"
aria-describedby="basic-addon1">
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon2">Password</span>
<input type="text" class="form-control" name="Password" placeholder="Password" aria-label="Username"
aria-describedby="basic-addon1">
</div>
<div class="mb-3">
<label class="lable" for="email">이메일</label>
<input type="email" class="form-control" id="email" name="email" placeholder="you@example.com" required/>
<div class="invalid-feedback"> 올바른 이메일 양식이 아닙니다. 다시 확인해주세요. </div>
<input type="text" class="form-control" id="certificationCode" placeholder="인증코드를 입력해주세요." style="margin-top: 10px; display:none" required/>
<div class="invalid-feedback"> 올바른 인증코드 양식이 아닙니다. 다시 확인해주세요. </div>
<input type="hidden" name="checkedEmail" value="">
<button class="btn btn-lg btn-block" id="codeCheck" onclick="checkCode()"
style="border-color: #95E1D3; color: #95E1D3; margin-top:10px;"
type="button">인증코드 제출</button>
<button class="btn btn-lg btn-block" id="emailCheck" onclick="certification();"
style="border-color: #95E1D3; color: #95E1D3; margin-top:10px"
type="button">인증하기</button>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon3">Username</span>
<input type="text" class="form-control" name="username" placeholder="Username" aria-label="Username"
aria-describedby="basic-addon1">
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon4">Intro</span>
<input type="text" class="form-control" name="Intro" placeholder="Intro" aria-label="Username"
aria-describedby="basic-addon1">
</div>
<div>
<input id="signupSuccess" type="submit" value="확인">
<button type="button" class="cancelBtn" onclick="cancel()">취소</button>
</div>
</form>
</div>
</body>
</html>
<script>
function cancel() {
window.location.href = "/";
}
$('#signupSuccess').prop("disabled", true);
$('#codeCheck').hide();
$('#emailCheck').click(function () {
$('#codeCheck').show();
})
//이메일 인증
function certification(){
if($('#email').val() == '') {
alert('인증코드를 받을 이메일을 입력해주세요.')
return;
}else{
//인증코드 입력란 노출
$('#certificationCode').show();
}
//입력한 이메일값이 바뀔 때
$('#email').change(function(){
$('#emailCheck').show();
$('#certificationCode').hide();
})
//이메일 인증 메일 보내기
$.ajax({
url: "/user/signup/mailConfirm",
type: "POST",
data: {"email" : $("#email").val()},
success: function(data){
alert("메일이 발송되었습니다.");
$("#certificationCode").val('');//기존에 값이 있었으면 지워줌
$("#emailCheck").hide();
// console.log(data);
}
});
}
function checkCode() {
// 입력한 인증번호
var userCode = $("#certificationCode").val();
// 서버로 인증번호 확인 요청
$.ajax({
url: "/user/signup/checkCode",
type: "POST",
data: { "code": userCode },
success: function (response) {
if (response === "success") {
// 인증코드가 유효한 경우 회원가입 처리
alert("인증되었습니다.");
$('#signupSuccess').prop("disabled", false);
$('#codeCheck').hide();
} else {
// 인증코드가 유효하지 않은 경우 처리
alert("유효하지 않은 인증코드입니다. 다시 시도해주세요.");
}
}
});
}
</script>
간단하게 구현한 html 코드이다.
certification() 코드로 조건문을 통해서 이메일주소값을 확인한후 공백이면 경고를 반환한다. 만약 문자를 하나라도 등록할시 인증코드 입력이 노출되게 show를 주었다.
그리고 인증코드 전송이라는 폼이 뜨게 설정하였다.
그리고 ajax를 통해 url을 받아오고 타입은 POST로 하였으니 설정한후에 data로 이메일 밸류값을 받아온다음 그값이 유효하면 메일이 발송되었습니다라는 알림창을 반환후 인증번호 전송이라는 폼을 hide로 숨겨놓게 설정하였다.
그후 인증코드 확인이라는 폼이 새로 생성되게 설정된다.
그리고 checkCode라는 메서드가 실행되었을 경우 인증코드 확인창의 밸류값을 받아온후 userCode 변수에 저장한다.
그리고 ajax를 통해 url로 http://user/signup/checkCode를 받아오고 타입은 똑같이 POST로 그리고 data로 userCode 변수에 담겨진 코드를 받아온후 파라미터로 response를 받아온다. 그리고 조건문으로 response가 success일 경우 알림으로 인증되었습니다.라는 문자를 반환후 잠겨있던 회원가입 버튼이 활성화가 되어 회원가입이 가능해지고 인증코드 확인 폼은 없어지게 설정하였다 반대로 인증코드가 틀렸을경우 경고창이 반환된다.
오늘은 이메일 인증에 대해서 코드를 작성하고 구현해보는 시간을 가졌다. 이대로 팀 프로젝트가 잘 마무리 되길 바란다.
'Spring' 카테고리의 다른 글
| JUnit, Mockito 내용정리 (0) | 2023.07.11 |
|---|---|
| 프로필 관리 기능 구현 (0) | 2023.07.06 |
| Spring Security,JWT,REST 내용정리 (0) | 2023.06.26 |
| JWT 내용정리 (0) | 2023.06.19 |
| 컨트롤러에 집중된 메서드를 삼계층으로 분리하는 법 (1) | 2023.06.16 |