Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions back/.env.default
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
CUSTOM__JWT__SECRET_KEY=NEED_TO_SET
DB_USERNAME=NEED_TO_SET
DB_PASSWORD=NEED_TO_SET
MAIL_USERNAME=NEED_TO_SET
MAIL_PASSWORD=NEED_TO_SET
1 change: 1 addition & 0 deletions back/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation ("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-mail")

// QueryDSL
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.back.domain.member.member.email;

import com.back.global.exception.ServiceException;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;

@Slf4j
@Service
@RequiredArgsConstructor
public class EmailService {
private final JavaMailSender mailSender;

/**
* ๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ ์ด๋ฉ”์ผ ๋ฐœ์†ก
*/
public void sendSimpleEmail(String to, String subject, String text) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

helper.setFrom("[email protected]", "JobMate");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text, false); // false = plain text

mailSender.send(message);
log.info("ํ…์ŠคํŠธ ์ด๋ฉ”์ผ ๋ฐœ์†ก ์„ฑ๊ณต: {}", to);
} catch (MessagingException | UnsupportedEncodingException e) {
log.error("ํ…์ŠคํŠธ ์ด๋ฉ”์ผ ๋ฐœ์†ก ์‹คํŒจ: {}", to, e);
throw new ServiceException("500-1", "ํ…์ŠคํŠธ ์ด๋ฉ”์ผ ๋ฐœ์†ก์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
}
}

/**
* HTML ์ด๋ฉ”์ผ ๋ฐœ์†ก
*/
public void sendHtmlEmail(String to, String subject, String htmlContent) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

helper.setFrom("[email protected]", "JobMate");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true); // true = HTML

mailSender.send(message);
log.info("HTML ์ด๋ฉ”์ผ ๋ฐœ์†ก ์„ฑ๊ณต: {}", to);
} catch (MessagingException | UnsupportedEncodingException e) {
log.error("HTML ์ด๋ฉ”์ผ ๋ฐœ์†ก ์‹คํŒจ: {}", to, e);
throw new ServiceException("500-2", "HTML ์ด๋ฉ”์ผ ๋ฐœ์†ก์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
}
}

/**
* ์ธ์ฆ๋ฒˆํ˜ธ ์ด๋ฉ”์ผ ๋ฐœ์†ก (๋ฉ˜ํ†  ํšŒ์›๊ฐ€์ž…์šฉ)
*/
public void sendVerificationCode(String to, String verificationCode) {
String subject = "[FiveLogic] ๋ฉ˜ํ†  ํšŒ์›๊ฐ€์ž… ์ธ์ฆ๋ฒˆํ˜ธ";
String htmlContent = buildVerificationEmailHtml(verificationCode);
sendHtmlEmail(to, subject, htmlContent);
}

/**
* ์ธ์ฆ๋ฒˆํ˜ธ ์ด๋ฉ”์ผ HTML ํ…œํ”Œ๋ฆฟ
*/
private String buildVerificationEmailHtml(String verificationCode) {
return """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
.content { padding: 30px; background-color: #f9f9f9; }
.code-box { background-color: #fff; border: 2px solid #4CAF50; padding: 20px; text-align: center; margin: 20px 0; }
.code { font-size: 32px; font-weight: bold; color: #4CAF50; letter-spacing: 5px; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>JobMate ๋ฉ˜ํ†  ํšŒ์›๊ฐ€์ž…</h1>
</div>
<div class="content">
<h2>์ธ์ฆ๋ฒˆํ˜ธ ํ™•์ธ</h2>
<p>์•ˆ๋…•ํ•˜์„ธ์š”. FiveLogic์ž…๋‹ˆ๋‹ค.</p>
<p>๋ฉ˜ํ†  ํšŒ์›๊ฐ€์ž…์„ ์œ„ํ•œ ์ธ์ฆ๋ฒˆํ˜ธ๋ฅผ ์•ˆ๋‚ด๋“œ๋ฆฝ๋‹ˆ๋‹ค.</p>
<p>์•„๋ž˜ ์ธ์ฆ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํšŒ์›๊ฐ€์ž…์„ ์™„๋ฃŒํ•ด์ฃผ์„ธ์š”.</p>

<div class="code-box">
<div class="code">%s</div>
</div>

<p><strong>โ€ป ์ธ์ฆ๋ฒˆํ˜ธ๋Š” 5๋ถ„๊ฐ„ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.</strong></p>
<p>๋ณธ์ธ์ด ์š”์ฒญํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ์ด ๋ฉ”์ผ์„ ๋ฌด์‹œํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค.</p>
</div>
<div class="footer">
<p>ยฉ 2025 FiveLogic. All rights reserved.</p>
</div>
</div>
</body>
</html>
""".formatted(verificationCode);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.back.domain.member.member.verification;

import com.back.domain.member.member.email.EmailService;
import com.back.global.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -14,6 +15,7 @@
@Slf4j
public class EmailVerificationService {
private final VerificationCodeStore codeStore;
private final EmailService emailService;
private static final Duration CODE_TTL = Duration.ofMinutes(5);
private static final SecureRandom random = new SecureRandom();

Expand All @@ -24,8 +26,9 @@ public String generateAndSendCode(String email) {
// ์ €์žฅ
codeStore.saveCode(email, code, CODE_TTL);

// TODO: ์‹ค์ œ ์ด๋ฉ”์ผ ๋ฐœ์†ก ๋กœ์ง ์ถ”๊ฐ€ (JavaMailSender)
log.info("Generated verification code for {}: {}", email, code);
// ์ด๋ฉ”์ผ ๋ฐœ์†ก
emailService.sendVerificationCode(email, code);
log.info("Generated and sent verification code for {}: {}", email, code);

return code; // ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ ๋ฐ˜ํ™˜ (์‹ค์ œ๋กœ๋Š” ์ด๋ฉ”์ผ๋กœ๋งŒ ์ „์†ก)
}
Expand Down
15 changes: 15 additions & 0 deletions back/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ spring:
use_sql_comments: true
default_batch_fetch_size: 100
open-in-view: false
mail:
host: smtp.gmail.com
port: 587
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000

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