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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,37 @@
import java.time.LocalDateTime;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import sequence.sequence_member.member.entity.EmailAuthTokenEntity;
import sequence.sequence_member.member.repository.EmailAuthTokenRepository;
import org.springframework.beans.factory.annotation.Value;


@RequiredArgsConstructor
@Service
public class EmailAuthService {

private final JavaMailSender mailSender;
private final EmailAuthTokenRepository tokenRepo;
private final SpringTemplateEngine templateEngine;

@Value("${NAVER_MAIL_USERNAME:dev_mj_@naver.com}")
private String fromEmail;
private final EmailAuthTokenRepository tokenRepo;

public void requestEmailVerification(String email) {
tokenRepo.findAllByEmail(email).forEach(token -> {
token.setExpired(true);
tokenRepo.save(token);
});

String token = UUID.randomUUID().toString().substring(0, 6);
String token = UUID.randomUUID().toString().substring(0, 6).toUpperCase();

EmailAuthTokenEntity emailAuthToken = EmailAuthTokenEntity.builder()
.email(email)
Expand All @@ -41,11 +43,9 @@ public void requestEmailVerification(String email) {
.build();

tokenRepo.save(emailAuthToken);

sendAuthEmail(email, token);
}

// 인증 확인
public void verifyEmailToken(String email, String token) {
EmailAuthTokenEntity authToken = tokenRepo.findByEmailAndToken(email, token)
.orElseThrow(() -> new IllegalArgumentException("잘못된 인증 정보입니다."));
Expand All @@ -61,42 +61,33 @@ public void verifyEmailToken(String email, String token) {
throw new IllegalStateException("시간 초과로 인하여 토큰이 만료되었습니다.");
}

// 해당 이메일의 모든 토큰 만료 처리
tokenRepo.findAllByEmail(email).forEach(t -> {
t.setVerified(false); // 기존 것들 다 false로 초기화
t.setVerified(false);
tokenRepo.save(t);
});

// 현재 토큰만 인증 처리
authToken.setVerified(true);
tokenRepo.save(authToken);
}



// MimeMessage 방식으로 인증 메일 전송
private void sendAuthEmail(String email, String token) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

Context context = new Context();
context.setVariable("token", token);

String htmlContent = templateEngine.process("emailAuth", context);

helper.setFrom(fromEmail);
helper.setTo(email);
helper.setSubject("[Sequence] 이메일 인증 코드");
helper.setSubject("[Sequence] 이메일 인증 안내");
helper.setText(htmlContent, true);

String content = "<html><body>"
+ "<h2>이메일 인증 안내</h2>"
+ "<p>아래의 인증 코드를 입력해주세요.</p>"
+ "<p><strong>인증 코드: " + token + "</strong></p>"
+ "</body></html>";

helper.setText(content, true);
mailSender.send(message);

} catch (MessagingException e) {
throw new RuntimeException("이메일 전송 실패", e);
throw new RuntimeException("이메일 발송 중 오류가 발생했습니다.", e);
}
}

}

43 changes: 43 additions & 0 deletions sequence_member/src/main/resources/templates/emailAuth.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Sequence 이메일 인증</title>
</head>

<body style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; margin: 0; padding: 0; background-color: #121212; width: 100%;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 40px 20px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="max-width: 580px; color: #E0E0E0;">
<tr>
<td align="left" style="padding-bottom: 20px;">
<h1 style="color: #D9230F; font-family: 'Georgia', 'Times New Roman', Times, serif; font-size: 36px; margin: 0;">Sequence</h1>
</td>
</tr>
<tr>
<td style="padding: 30px; background-color: #1e1e1e; border-radius: 8px;">
<h2 style="font-size: 24px; color: #D9230F; margin: 0 0 20px 0;">이메일 인증 안내</h2>
<p style="font-size: 16px; line-height: 1.6; color: #BDBDBD; margin: 0 0 20px 0;">회원가입을 완료하려면 아래 인증 코드를 입력해주세요.</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #2a2a2a; border-radius: 8px; margin: 20px 0;">
<tr>
<td align="center" style="padding: 25px;">
<span style="font-size: 28px; font-weight: bold; color: #FFFFFF; letter-spacing: 5px;" th:text="${token}">A1B2C3</span>
</td>
</tr>
</table>
<p style="font-size: 14px; line-height: 1.6; color: #BDBDBD;">본인이 요청하지 않은 인증 메일이라면 이 메일을 무시하셔도 좋습니다.</p>
</td>
</tr>
<tr>
<td align="center" style="font-size: 12px; color: #888888; padding: 30px 0;">
<p style="margin: 0;">&copy; <span th:text="${#dates.year(#dates.createNow())}">2025</span> Sequence. All Rights Reserved.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
Loading