Skip to content

Commit

Permalink
Merge pull request #7 from Domitory-CheckMate/feature/6-user
Browse files Browse the repository at this point in the history
[feat] 이메일 인증 기능 구현
  • Loading branch information
ziiyouth authored Dec 27, 2023
2 parents 5af1f6e + 8fa784c commit 8a30ddd
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 3 deletions.
1 change: 0 additions & 1 deletion .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ jobs:

- name: 🐧 application.yml 파일을 생성 합니다.
run: |
mkdir ./src/main/resources
touch ./src/main/resources/application.yml
echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml # github actions에서 설정한 값을 application.yml 파일에 쓰기
Expand Down
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" // querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 대응 코드
annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 대응 코드

// 이메일 SMTP
implementation 'org.springframework.boot:spring-boot-starter-mail'

// thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.gachon.checkmate.domain.member.controller;

import lombok.RequiredArgsConstructor;
import org.gachon.checkmate.domain.member.dto.request.EmailPostRequestDto;
import org.gachon.checkmate.domain.member.dto.response.EmailResponseDto;
import org.gachon.checkmate.domain.member.service.MemberService;
import org.gachon.checkmate.global.common.SuccessResponse;
import org.springframework.http.ResponseEntity;
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.RestController;

@RequiredArgsConstructor
@RequestMapping("/api/member")
@RestController
public class MemberController {

private final MemberService memberService;

@PostMapping("/email")
public ResponseEntity<SuccessResponse<?>> sendMail(@RequestBody final EmailPostRequestDto emailPostRequestDto) {
final EmailResponseDto emailResponseDto = memberService.sendMail(emailPostRequestDto);
return SuccessResponse.ok(emailResponseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.gachon.checkmate.domain.member.dto.request;

public record EmailPostRequestDto(
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.gachon.checkmate.domain.member.dto.response;

public record EmailResponseDto(
String code
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.gachon.checkmate.domain.member.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gachon.checkmate.domain.member.dto.request.EmailPostRequestDto;
import org.gachon.checkmate.domain.member.dto.response.EmailResponseDto;
import org.gachon.checkmate.global.config.auth.jwt.JwtProvider;
import org.gachon.checkmate.global.config.mail.MailProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@RequiredArgsConstructor
@Transactional
@Service
public class MemberService {

private final JwtProvider jwtProvider;
private final MailProvider mailProvider;

public EmailResponseDto sendMail(EmailPostRequestDto emailPostRequestDto) {
String authNum = mailProvider.sendMail(emailPostRequestDto.email(), "email");
return new EmailResponseDto(authNum);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class SecurityConfig {
private final CorsConfig corsConfig;
private final JwtProvider jwtProvider;

private static final String[] whiteList = {"/"};
private static final String[] whiteList = {"/", "api/member/email"};

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.gachon.checkmate.global.config.mail;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gachon.checkmate.domain.member.dto.request.EmailPostRequestDto;
import org.gachon.checkmate.global.error.exception.InternalServerException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

import java.util.Random;

import static org.gachon.checkmate.global.error.ErrorCode.EMAIL_SEND_ERROR;

@Slf4j
@RequiredArgsConstructor
@Component
public class MailProvider {

private final JavaMailSender javaMailSender;
private final SpringTemplateEngine templateEngine;

public String sendMail(String email, String type) {
String authNum = createNumericCode();
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8");
mimeMessageHelper.setTo(email); // 메일 수신자
mimeMessageHelper.setSubject("[CHECKMATE] 이메일 인증번호 발송"); // 메일 제목
mimeMessageHelper.setText(setContext(authNum, type), true); // 메일 본문
javaMailSender.send(mimeMessage);
return authNum;
} catch (MessagingException e) {
throw new InternalServerException(EMAIL_SEND_ERROR);
}
}

private static String createNumericCode() {
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < 6; i++) {
code.append(random.nextInt(10));
}
return code.toString();
}

private String setContext(String code, String type) {
Context context = new Context();
context.setVariable("code", code);
return templateEngine.process(type, context);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public enum ErrorCode {
/**
* 500 Internal Server Error
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."),
EMAIL_SEND_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "이메일 전송에 실패했습니다.");

private final HttpStatus httpStatus;
private final String message;
Expand Down
18 changes: 18 additions & 0 deletions src/main/resources/templates/email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<body style="font-family: Arial, sans-serif; background-color: #f8f8f8; text-align: center;">

<div style="max-width: 600px; margin: 100px auto; padding: 20px; background-color: #ffffff; border: 1px solid #ff6600; border-radius: 10px;">
<p style="color: #ff6600;"><strong>기숙사 룸메이트 매칭 플랫폼 Check-mate</strong></p>
<p style="color: #000000;">반갑습니다.</p>
<p>아래 코드를 인증번호 입력 란에 입력해주세요.</p>

<div style="font-size: 24px; margin-top: 20px; padding: 10px; background-color: #ff6600; color: #ffffff; border-radius: 5px;">
<span th:text="${code}"></span>
</div>
</div>

</body>

</html>

0 comments on commit 8a30ddd

Please sign in to comment.