Skip to content

Commit 945ee9c

Browse files
authored
feat:이메일 인증 추가 (#165)
1 parent de60944 commit 945ee9c

File tree

5 files changed

+139
-2
lines changed

5 files changed

+139
-2
lines changed

back/.env.default

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
CUSTOM__JWT__SECRET_KEY=NEED_TO_SET
22
DB_USERNAME=NEED_TO_SET
33
DB_PASSWORD=NEED_TO_SET
4+
MAIL_USERNAME=NEED_TO_SET
5+
MAIL_PASSWORD=NEED_TO_SET

back/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
implementation("org.springframework.boot:spring-boot-starter-security")
4040
implementation("org.springframework.boot:spring-boot-starter-web")
4141
implementation ("org.springframework.boot:spring-boot-starter-data-redis")
42+
implementation("org.springframework.boot:spring-boot-starter-mail")
4243

4344
// QueryDSL
4445
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.back.domain.member.member.email;
2+
3+
import com.back.global.exception.ServiceException;
4+
import jakarta.mail.MessagingException;
5+
import jakarta.mail.internet.MimeMessage;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.mail.javamail.JavaMailSender;
9+
import org.springframework.mail.javamail.MimeMessageHelper;
10+
import org.springframework.stereotype.Service;
11+
12+
import java.io.UnsupportedEncodingException;
13+
14+
@Slf4j
15+
@Service
16+
@RequiredArgsConstructor
17+
public class EmailService {
18+
private final JavaMailSender mailSender;
19+
20+
/**
21+
* 간단한 텍스트 이메일 발송
22+
*/
23+
public void sendSimpleEmail(String to, String subject, String text) {
24+
try {
25+
MimeMessage message = mailSender.createMimeMessage();
26+
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
27+
28+
helper.setFrom("[email protected]", "JobMate");
29+
helper.setTo(to);
30+
helper.setSubject(subject);
31+
helper.setText(text, false); // false = plain text
32+
33+
mailSender.send(message);
34+
log.info("텍스트 이메일 발송 성공: {}", to);
35+
} catch (MessagingException | UnsupportedEncodingException e) {
36+
log.error("텍스트 이메일 발송 실패: {}", to, e);
37+
throw new ServiceException("500-1", "텍스트 이메일 발송에 실패했습니다.");
38+
}
39+
}
40+
41+
/**
42+
* HTML 이메일 발송
43+
*/
44+
public void sendHtmlEmail(String to, String subject, String htmlContent) {
45+
try {
46+
MimeMessage message = mailSender.createMimeMessage();
47+
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
48+
49+
helper.setFrom("[email protected]", "JobMate");
50+
helper.setTo(to);
51+
helper.setSubject(subject);
52+
helper.setText(htmlContent, true); // true = HTML
53+
54+
mailSender.send(message);
55+
log.info("HTML 이메일 발송 성공: {}", to);
56+
} catch (MessagingException | UnsupportedEncodingException e) {
57+
log.error("HTML 이메일 발송 실패: {}", to, e);
58+
throw new ServiceException("500-2", "HTML 이메일 발송에 실패했습니다.");
59+
}
60+
}
61+
62+
/**
63+
* 인증번호 이메일 발송 (멘토 회원가입용)
64+
*/
65+
public void sendVerificationCode(String to, String verificationCode) {
66+
String subject = "[FiveLogic] 멘토 회원가입 인증번호";
67+
String htmlContent = buildVerificationEmailHtml(verificationCode);
68+
sendHtmlEmail(to, subject, htmlContent);
69+
}
70+
71+
/**
72+
* 인증번호 이메일 HTML 템플릿
73+
*/
74+
private String buildVerificationEmailHtml(String verificationCode) {
75+
return """
76+
<!DOCTYPE html>
77+
<html>
78+
<head>
79+
<meta charset="UTF-8">
80+
<style>
81+
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
82+
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
83+
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
84+
.content { padding: 30px; background-color: #f9f9f9; }
85+
.code-box { background-color: #fff; border: 2px solid #4CAF50; padding: 20px; text-align: center; margin: 20px 0; }
86+
.code { font-size: 32px; font-weight: bold; color: #4CAF50; letter-spacing: 5px; }
87+
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
88+
</style>
89+
</head>
90+
<body>
91+
<div class="container">
92+
<div class="header">
93+
<h1>JobMate 멘토 회원가입</h1>
94+
</div>
95+
<div class="content">
96+
<h2>인증번호 확인</h2>
97+
<p>안녕하세요. FiveLogic입니다.</p>
98+
<p>멘토 회원가입을 위한 인증번호를 안내드립니다.</p>
99+
<p>아래 인증번호를 입력하여 회원가입을 완료해주세요.</p>
100+
101+
<div class="code-box">
102+
<div class="code">%s</div>
103+
</div>
104+
105+
<p><strong>※ 인증번호는 5분간 유효합니다.</strong></p>
106+
<p>본인이 요청하지 않은 경우, 이 메일을 무시하셔도 됩니다.</p>
107+
</div>
108+
<div class="footer">
109+
<p>© 2025 FiveLogic. All rights reserved.</p>
110+
</div>
111+
</div>
112+
</body>
113+
</html>
114+
""".formatted(verificationCode);
115+
}
116+
}

back/src/main/java/com/back/domain/member/member/verification/EmailVerificationService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.domain.member.member.verification;
22

3+
import com.back.domain.member.member.email.EmailService;
34
import com.back.global.exception.ServiceException;
45
import lombok.RequiredArgsConstructor;
56
import lombok.extern.slf4j.Slf4j;
@@ -14,6 +15,7 @@
1415
@Slf4j
1516
public class EmailVerificationService {
1617
private final VerificationCodeStore codeStore;
18+
private final EmailService emailService;
1719
private static final Duration CODE_TTL = Duration.ofMinutes(5);
1820
private static final SecureRandom random = new SecureRandom();
1921

@@ -24,8 +26,9 @@ public String generateAndSendCode(String email) {
2426
// 저장
2527
codeStore.saveCode(email, code, CODE_TTL);
2628

27-
// TODO: 실제 이메일 발송 로직 추가 (JavaMailSender)
28-
log.info("Generated verification code for {}: {}", email, code);
29+
// 이메일 발송
30+
emailService.sendVerificationCode(email, code);
31+
log.info("Generated and sent verification code for {}: {}", email, code);
2932

3033
return code; // 테스트용으로 반환 (실제로는 이메일로만 전송)
3134
}

back/src/main/resources/application.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ spring:
4242
use_sql_comments: true
4343
default_batch_fetch_size: 100
4444
open-in-view: false
45+
mail:
46+
host: smtp.gmail.com
47+
port: 587
48+
username: ${MAIL_USERNAME}
49+
password: ${MAIL_PASSWORD}
50+
properties:
51+
mail:
52+
smtp:
53+
auth: true
54+
starttls:
55+
enable: true
56+
required: true
57+
connectiontimeout: 5000
58+
timeout: 5000
59+
writetimeout: 5000
4560

4661
springdoc: #Swagger ??
4762
default-produces-media-type: application/json;charset=UTF-8

0 commit comments

Comments
 (0)