diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index c5adf87..046ccd8 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -119,3 +119,26 @@ include::{snippetsDir}/emailDuplicationCheck/1/http-response.adoc[] include::{snippetsDir}/emailDuplicationCheck/1/response-fields.adoc[] --- + + +=== **2. 이메일 인증 api** + +이메일 인증용 코드를 발송하는 api입니다. + +==== Request +include::{snippetsDir}/emailAuthentication/1/http-request.adoc[] + +==== Request Body Fields +include::{snippetsDir}/emailAuthentication/1/request-fields.adoc[] + +==== 성공 Response +include::{snippetsDir}/emailAuthentication/1/http-response.adoc[] + +==== Response Body Fields +include::{snippetsDir}/emailAuthentication/1/response-fields.adoc[] + +==== 실패 Response +실패1. +include::{snippetsDir}/emailAuthentication/2/http-response.adoc[] +실패 2 +include::{snippetsDir}/emailAuthentication/3/http-response.adoc[] diff --git a/src/main/java/com/ftm/server/adapter/controller/user/EmailAuthenticationController.java b/src/main/java/com/ftm/server/adapter/controller/user/EmailAuthenticationController.java new file mode 100644 index 0000000..f7ed1a2 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/controller/user/EmailAuthenticationController.java @@ -0,0 +1,30 @@ +package com.ftm.server.adapter.controller.user; + +import com.ftm.server.adapter.dto.request.EmailAuthenticationRequest; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import com.ftm.server.domain.dto.command.EmailAuthenticationCommand; +import com.ftm.server.domain.usecase.user.EmailAuthenticationUseCase; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +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.RestController; + +@RequiredArgsConstructor +@RestController +public class EmailAuthenticationController { + + private final EmailAuthenticationUseCase emailAuthenticationUseCase; + + @PostMapping("/api/users/email/authentication") + public ResponseEntity> emailAuthenticationCodeSender( + @Valid @RequestBody EmailAuthenticationRequest request) { + emailAuthenticationUseCase.sendEmailAuthenticationCode( + EmailAuthenticationCommand.from(request)); // command 객체 전달 + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessResponseCode.OK)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/dto/request/.gitkeep b/src/main/java/com/ftm/server/adapter/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/ftm/server/adapter/dto/request/EmailAuthenticationRequest.java b/src/main/java/com/ftm/server/adapter/dto/request/EmailAuthenticationRequest.java new file mode 100644 index 0000000..bf68f7a --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/dto/request/EmailAuthenticationRequest.java @@ -0,0 +1,13 @@ +package com.ftm.server.adapter.dto.request; + +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +@Data +public class EmailAuthenticationRequest { + + @Pattern( + regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + message = "이메일 형식이 올바르지 않습니다.") + private final String email; +} diff --git a/src/main/java/com/ftm/server/adapter/gateway/MailSenderGateway.java b/src/main/java/com/ftm/server/adapter/gateway/MailSenderGateway.java new file mode 100644 index 0000000..cb97f90 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/gateway/MailSenderGateway.java @@ -0,0 +1,6 @@ +package com.ftm.server.adapter.gateway; + +public interface MailSenderGateway { + + void sendEmail(String to, String code); +} diff --git a/src/main/java/com/ftm/server/adapter/gateway/repository/EmailVerificationLogsRepository.java b/src/main/java/com/ftm/server/adapter/gateway/repository/EmailVerificationLogsRepository.java new file mode 100644 index 0000000..d4e6df5 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/gateway/repository/EmailVerificationLogsRepository.java @@ -0,0 +1,13 @@ +package com.ftm.server.adapter.gateway.repository; + +import com.ftm.server.entity.entities.EmailVerificationLogs; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmailVerificationLogsRepository + extends JpaRepository { + + Optional findByEmail(String email); +} diff --git a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java index 56e0b41..03df641 100644 --- a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java +++ b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java @@ -22,9 +22,11 @@ public enum ErrorResponseCode { // 409번 USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "E409_001", "이미 존재하는 사용자입니다."), PASSWORD_NOT_MATCHED(HttpStatus.CONFLICT, "E409_002", "비밀번호가 일치하지 않습니다."), + EXCEED_NUMBER_OF_TRIAL(HttpStatus.CONFLICT, "E409_003", "시도 가능 횟수를 초과했습니다. 잠시 후에 다시 시도 해 주세요."), // 500번 - UNKNOWN_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E500_001", "알 수 없는 서버 에러가 발생했습니다."); + UNKNOWN_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E500_001", "알 수 없는 서버 에러가 발생했습니다."), + FAIL_TO_SEND_EMAIL(HttpStatus.INTERNAL_SERVER_ERROR, "E500_002", "서버 내부 문제로 메일 전송에 실패했습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/com/ftm/server/common/utils/.gitkeep b/src/main/java/com/ftm/server/common/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/ftm/server/common/utils/RandomCodeCreator.java b/src/main/java/com/ftm/server/common/utils/RandomCodeCreator.java new file mode 100644 index 0000000..d9e1925 --- /dev/null +++ b/src/main/java/com/ftm/server/common/utils/RandomCodeCreator.java @@ -0,0 +1,22 @@ +package com.ftm.server.common.utils; + +import java.security.SecureRandom; +import org.springframework.stereotype.Component; + +@Component +public class RandomCodeCreator { + + private static final int CODE_LENGTH = 6; + + private final SecureRandom random = new SecureRandom(); + + public String generateAuthCode() { + + StringBuilder sb = new StringBuilder(CODE_LENGTH); + for (int i = 0; i < CODE_LENGTH; i++) { + int digit = random.nextInt(10); + sb.append(digit); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/ftm/server/domain/dto/command/EmailAuthenticationCommand.java b/src/main/java/com/ftm/server/domain/dto/command/EmailAuthenticationCommand.java new file mode 100644 index 0000000..1a5edd6 --- /dev/null +++ b/src/main/java/com/ftm/server/domain/dto/command/EmailAuthenticationCommand.java @@ -0,0 +1,13 @@ +package com.ftm.server.domain.dto.command; + +import com.ftm.server.adapter.dto.request.EmailAuthenticationRequest; +import lombok.Data; + +@Data +public class EmailAuthenticationCommand { + private final String email; + + public static EmailAuthenticationCommand from(EmailAuthenticationRequest request) { + return new EmailAuthenticationCommand(request.getEmail()); + } +} diff --git a/src/main/java/com/ftm/server/domain/dto/command/EmailVerificationLogCreationCommand.java b/src/main/java/com/ftm/server/domain/dto/command/EmailVerificationLogCreationCommand.java new file mode 100644 index 0000000..c325ef5 --- /dev/null +++ b/src/main/java/com/ftm/server/domain/dto/command/EmailVerificationLogCreationCommand.java @@ -0,0 +1,13 @@ +package com.ftm.server.domain.dto.command; + +import lombok.Data; + +@Data +public class EmailVerificationLogCreationCommand { + private final String email; + private final String verificationCode; + + public static EmailVerificationLogCreationCommand of(String email, String verificationCode) { + return new EmailVerificationLogCreationCommand(email, verificationCode); + } +} diff --git a/src/main/java/com/ftm/server/domain/service/EmailVerificationLogsService.java b/src/main/java/com/ftm/server/domain/service/EmailVerificationLogsService.java new file mode 100644 index 0000000..b54b2a6 --- /dev/null +++ b/src/main/java/com/ftm/server/domain/service/EmailVerificationLogsService.java @@ -0,0 +1,24 @@ +package com.ftm.server.domain.service; + +import com.ftm.server.adapter.gateway.repository.EmailVerificationLogsRepository; +import com.ftm.server.domain.dto.command.EmailVerificationLogCreationCommand; +import com.ftm.server.domain.dto.query.FindByEmailQuery; +import com.ftm.server.entity.entities.EmailVerificationLogs; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailVerificationLogsService { + private final EmailVerificationLogsRepository emailVerificationLogsRepository; + + public Optional findEmailVerificationLogsByEmail( + FindByEmailQuery query) { + return emailVerificationLogsRepository.findByEmail(query.getEmail()); + } + + public void saveEmailVerificationLogs(EmailVerificationLogCreationCommand command) { + emailVerificationLogsRepository.save(EmailVerificationLogs.from(command)); + } +} diff --git a/src/main/java/com/ftm/server/domain/usecase/user/EmailAuthenticationUseCase.java b/src/main/java/com/ftm/server/domain/usecase/user/EmailAuthenticationUseCase.java new file mode 100644 index 0000000..d2f2bd1 --- /dev/null +++ b/src/main/java/com/ftm/server/domain/usecase/user/EmailAuthenticationUseCase.java @@ -0,0 +1,57 @@ +package com.ftm.server.domain.usecase.user; + +import com.ftm.server.adapter.gateway.MailSenderGateway; +import com.ftm.server.common.annotation.UseCase; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.common.utils.RandomCodeCreator; +import com.ftm.server.domain.dto.command.EmailAuthenticationCommand; +import com.ftm.server.domain.dto.command.EmailVerificationLogCreationCommand; +import com.ftm.server.domain.dto.query.FindByEmailQuery; +import com.ftm.server.domain.service.EmailVerificationLogsService; +import com.ftm.server.entity.entities.EmailVerificationLogs; +import jakarta.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.Optional; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class EmailAuthenticationUseCase { + + private final MailSenderGateway mailSenderGateway; + + private final RandomCodeCreator randomCodeCreator; + + private final EmailVerificationLogsService emailVerificationLogsService; + + @Transactional + public void sendEmailAuthenticationCode(EmailAuthenticationCommand command) { + + String authCode = randomCodeCreator.generateAuthCode(); + String email = command.getEmail(); + + Optional emailVerificationLogs = + emailVerificationLogsService.findEmailVerificationLogsByEmail( + FindByEmailQuery.of(email)); + + int MAX_TRIALS = 5; + int BLOCK_MINUTES = 15; + + if (emailVerificationLogs.isEmpty()) { // 이메일 인증을 한번도 시도하지 않은 경우 log 새로 생성 + emailVerificationLogsService.saveEmailVerificationLogs( + EmailVerificationLogCreationCommand.of(email, authCode)); + } else if (emailVerificationLogs.get().getTrialNum() + < MAX_TRIALS) { // 이메일 인증 시도를 한적 있으나 시도 횟수를 초과하지 않은 경우 log update + emailVerificationLogs.get().updateVerificationStatus(authCode); + } else if (emailVerificationLogs // 이메일 인증 시도 횟수를 초과했으나 마지막 시도 횟수로부터 15분 이상이 지난 경우 log 초기화 + .get() + .getTokenIssuanceTime() + .isBefore(LocalDateTime.now().minusMinutes(BLOCK_MINUTES))) { + emailVerificationLogs.get().initializeVerificationStatus(authCode); + } else { // 이메일 인증 시도 횟수를 단순히 초과한 경우 + throw new CustomException(ErrorResponseCode.EXCEED_NUMBER_OF_TRIAL); + } + mailSenderGateway.sendEmail(email, authCode); + } +} diff --git a/src/main/java/com/ftm/server/entity/entities/EmailVerificationLogs.java b/src/main/java/com/ftm/server/entity/entities/EmailVerificationLogs.java index 9ccbe81..6a02d19 100644 --- a/src/main/java/com/ftm/server/entity/entities/EmailVerificationLogs.java +++ b/src/main/java/com/ftm/server/entity/entities/EmailVerificationLogs.java @@ -1,5 +1,6 @@ package com.ftm.server.entity.entities; +import com.ftm.server.domain.dto.command.EmailVerificationLogCreationCommand; import jakarta.persistence.*; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -19,7 +20,6 @@ public class EmailVerificationLogs extends BaseEntity { @Column(nullable = false) private String email; - @Lob @Column(name = "verification_code", nullable = false) private String verificationCode; @@ -45,4 +45,26 @@ private EmailVerificationLogs( this.trialNum = trialNum; this.tokenIssuanceTime = tokenIssuanceTime; } + + public static EmailVerificationLogs from(EmailVerificationLogCreationCommand command) { + return EmailVerificationLogs.builder() + .email(command.getEmail()) + .verificationCode(command.getVerificationCode()) + .isVerified(false) + .trialNum(1) + .tokenIssuanceTime(LocalDateTime.now()) + .build(); + } + + public void updateVerificationStatus(String verificationCode) { + this.trialNum++; + this.verificationCode = verificationCode; + this.tokenIssuanceTime = LocalDateTime.now(); + } + + public void initializeVerificationStatus(String verificationCode) { + this.trialNum = 1; + this.verificationCode = verificationCode; + this.tokenIssuanceTime = LocalDateTime.now(); + } } diff --git a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java index ac4c2c3..50233ab 100644 --- a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java +++ b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java @@ -41,7 +41,11 @@ public class SecurityConfig { "https://dev-api.fittheman.site", // 개발 환경 서버 도메인 "https://fittheman.site"); // 개발 환경 클라이언트 도메인 - private static final String[] ANONYMOUS_MATCHERS = {"/docs/**", "/api/users/email/duplication"}; + private static final String[] GET_ANONYMOUS_MATCHERS = {"/api/users/email/duplication"}; + + private static final String[] POST_ANONYMOUS_MATCHERS = {"/api/users/email/authentication"}; + + private static final String[] ANONYMOUS_MATCHERS = {"/docs/**"}; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -75,8 +79,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests( authorize -> { authorize - // 정적 리소스 경로 허용 .requestMatchers(ANONYMOUS_MATCHERS) + .permitAll() // 정적 리소스 경로 허용 + .requestMatchers(HttpMethod.GET, GET_ANONYMOUS_MATCHERS) + .permitAll() + .requestMatchers(HttpMethod.POST, POST_ANONYMOUS_MATCHERS) .permitAll(); // TODO: 요청 허용 특정 API 추가 (회원가입, 로그인 등) diff --git a/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderConfig.java b/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderConfig.java new file mode 100644 index 0000000..b22c0e0 --- /dev/null +++ b/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderConfig.java @@ -0,0 +1,38 @@ +package com.ftm.server.infrastructure.smtp; + +import java.util.Properties; +import org.springframework.boot.autoconfigure.mail.MailProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +@Configuration +@EnableConfigurationProperties(MailProperties.class) +public class MailSenderConfig { + + private final MailProperties mailProperties; + + public MailSenderConfig(MailProperties mailProperties) { + this.mailProperties = mailProperties; + } + + @Bean + public JavaMailSender javaMailService() { + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); + javaMailSender.setHost(mailProperties.getHost()); + javaMailSender.setUsername(mailProperties.getUsername()); + javaMailSender.setPassword(mailProperties.getPassword()); + javaMailSender.setPort(mailProperties.getPort()); + + Properties properties = javaMailSender.getJavaMailProperties(); + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.starttls.enable", "true"); + + javaMailSender.setJavaMailProperties(properties); + javaMailSender.setDefaultEncoding("UTF-8"); + + return javaMailSender; + } +} diff --git a/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderService.java b/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderService.java new file mode 100644 index 0000000..90e950c --- /dev/null +++ b/src/main/java/com/ftm/server/infrastructure/smtp/MailSenderService.java @@ -0,0 +1,49 @@ +package com.ftm.server.infrastructure.smtp; + +import com.ftm.server.adapter.gateway.MailSenderGateway; +import com.ftm.server.common.annotation.InfraService; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.mail.MailException; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; + +@RequiredArgsConstructor +@InfraService +public class MailSenderService implements MailSenderGateway { + + private final JavaMailSender mailSender; + + @Override + public void sendEmail(String to, String code) { + MimeMessage message = mailSender.createMimeMessage(); + + String mailContent = + """ + + +

핏더맨 이메일 인증 코드입니다

+
%s
+

위 인증 코드를 입력하여 이메일 인증을 완료해 주세요.

+
+
+

이 메일은 핏더맨 시스템에 의해 자동 발송되었습니다.

+

문의:ftmanserver@gmail.com

+ + +""" + .formatted(code); + try { + MimeMessageHelper helper = new MimeMessageHelper(message, true); + helper.setTo(to); + helper.setSubject("[핏더맨] 이메일 인증을 완료해주세요!"); + helper.setText(mailContent, true); + mailSender.send(message); + } catch (MailException | MessagingException e) { + throw new CustomException((ErrorResponseCode.FAIL_TO_SEND_EMAIL), e.getMessage()); + } + } +} diff --git a/src/test/java/com/ftm/server/user/EmailAuthenticationTest.java b/src/test/java/com/ftm/server/user/EmailAuthenticationTest.java new file mode 100644 index 0000000..91d7f69 --- /dev/null +++ b/src/test/java/com/ftm/server/user/EmailAuthenticationTest.java @@ -0,0 +1,144 @@ +package com.ftm.server.user; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.mockito.Mockito.doThrow; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.dto.request.EmailAuthenticationRequest; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.dto.command.EmailAuthenticationCommand; +import com.ftm.server.domain.usecase.user.EmailAuthenticationUseCase; +import jakarta.transaction.Transactional; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.restdocs.snippet.Attributes; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.springframework.test.web.servlet.ResultActions; + +public class EmailAuthenticationTest extends BaseTest { + + @Autowired private EmailAuthenticationUseCase emailAuthenticationUseCase; + + @MockitoSpyBean private EmailAuthenticationUseCase mockitoEmailAuthenticationUseCase; + + private final List responseFieldDescriptors = + List.of( + fieldWithPath("status").type(JsonFieldType.NUMBER).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("메시지"), + fieldWithPath("data") + .type(JsonFieldType.OBJECT) + .optional() + .description("data") + .attributes(new Attributes.Attribute("nullable", "반환할 data 없음. Null"))); + + private final List requestFieldDescriptors = + List.of( + fieldWithPath("email") + .type(JsonFieldType.STRING) + .description("인증 email") + .attributes(new Attributes.Attribute("constraint", "이메일 형식"))); + + private ResultActions getResultActions(EmailAuthenticationRequest request) throws Exception { + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders.post("/api/users/email/authentication") + .contentType(MediaType.APPLICATION_JSON) // request body content type + .content(mapper.writeValueAsString(request))); + } + + // 문서화 반환 함수 + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "emailAuthentication/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + responseFields(responseFieldDescriptors), + requestFields(requestFieldDescriptors), + resource( + ResourceSnippetParameters.builder() + .tag("회원") + .summary("이메일 인증 api") + .description("email 인증용 코드를 발송하는 api입니다.") + .responseFields(responseFieldDescriptors) + .requestFields(requestFieldDescriptors) + .build())); + } + + @Transactional + @Test + void 이메일_인증코드_전송_성공() throws Exception { + // given + EmailAuthenticationRequest request = new EmailAuthenticationRequest("test@gmail.com"); + + // when + ResultActions resultActions = getResultActions(request); + + // then + resultActions.andExpect(status().isOk()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Transactional + @Test + void 이메일_인증코드_전송_실패1() throws Exception { + // given + EmailAuthenticationRequest request = new EmailAuthenticationRequest("test@gmail.com"); + for (int i = 0; i < 5; i++) { + emailAuthenticationUseCase.sendEmailAuthenticationCode( + EmailAuthenticationCommand.from(request)); + } + // when + ResultActions resultActions = getResultActions(request); + + // then + resultActions + .andExpect( + status().is( + ErrorResponseCode.EXCEED_NUMBER_OF_TRIAL + .getHttpStatus() + .value())) + .andExpect( + jsonPath("code").value(ErrorResponseCode.EXCEED_NUMBER_OF_TRIAL.getCode())); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Transactional + @Test + void 이메일_인증코드_전송_실패2() throws Exception { + // given + EmailAuthenticationRequest request = new EmailAuthenticationRequest("test@gmail.com"); + doThrow(new CustomException(ErrorResponseCode.FAIL_TO_SEND_EMAIL)) + .when(mockitoEmailAuthenticationUseCase) + .sendEmailAuthenticationCode(EmailAuthenticationCommand.from(request)); + + // when + ResultActions resultActions = getResultActions(request); + + // then + resultActions + .andExpect( + status().is(ErrorResponseCode.FAIL_TO_SEND_EMAIL.getHttpStatus().value())) + .andExpect(jsonPath("code").value(ErrorResponseCode.FAIL_TO_SEND_EMAIL.getCode())); + + // documentation + resultActions.andDo(getDocument(3)); + } +}