diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/EmailSentResponse.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/EmailSentResponse.java index 43e50e5..905f8ae 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/EmailSentResponse.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/EmailSentResponse.java @@ -1,8 +1,14 @@ package com.whereyouad.WhereYouAd.domains.user.application.dto.response; +import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; + +import java.util.List; + public record EmailSentResponse( String message, //인증코드를 이메일로 전송했습니다. String email, //전송한 이메일 - long expireIn //만료시간 (500L -> 500초) + Long expireIn, //만료시간 (500L -> 500초) + boolean isProviderLinked, + List providerTypes ) { } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/PasswordResetResponse.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/PasswordResetResponse.java new file mode 100644 index 0000000..ef4932d --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/PasswordResetResponse.java @@ -0,0 +1,8 @@ +package com.whereyouad.WhereYouAd.domains.user.application.dto.response; + +public record PasswordResetResponse( + String message, //인증코드를 이메일로 전송했습니다. + String email, //전송한 이메일 + Long expireIn //만료시간 (500L -> 500초) +) { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/mapper/UserConverter.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/mapper/UserConverter.java index a2a6e6c..b6e539c 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/mapper/UserConverter.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/mapper/UserConverter.java @@ -1,12 +1,17 @@ package com.whereyouad.WhereYouAd.domains.user.application.mapper; +import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; +import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse; +import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus; import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User; import com.whereyouad.WhereYouAd.global.security.oauth2.dto.OAuth2Response; import com.whereyouad.WhereYouAd.global.security.oauth2.dto.OAuth2UserInfo; +import java.util.List; + public class UserConverter { public static SignUpResponse toSignInResponse(User user) { @@ -45,4 +50,27 @@ public static MyPageResponse toMyPageResponse(User user, String provider) { provider ); } + + public static EmailSentResponse toEmailSentResponseSuccess(String email) { + return new EmailSentResponse("인증 코드를 이메일로 전송했습니다", + email, + 180L, + false, + null); + } + + public static EmailSentResponse toEmailSentResponseFail(String email, List providers) { + return new EmailSentResponse("이미 소셜 계정으로 가입된 이메일 입니다.", + email, + null, + true, + providers); + } + + public static PasswordResetResponse toPasswordResetResponse(String email) { + + return new PasswordResetResponse("인증 코드를 이메일로 전송했습니다.", + email, + 180L); + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/EmailService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/EmailService.java index ac54b53..2bd48cc 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/EmailService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/EmailService.java @@ -1,8 +1,13 @@ package com.whereyouad.WhereYouAd.domains.user.domain.service; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; +import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse; +import com.whereyouad.WhereYouAd.domains.user.application.mapper.UserConverter; +import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; +import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount; +import com.whereyouad.WhereYouAd.domains.user.persistence.repository.AuthProviderAccountRepository; import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository; import com.whereyouad.WhereYouAd.global.utils.RedisUtil; import lombok.RequiredArgsConstructor; @@ -14,6 +19,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.List; + @Transactional @Service @RequiredArgsConstructor @@ -23,6 +31,7 @@ public class EmailService { private final JavaMailSender emailSender; private final RedisUtil redisUtil; private final UserRepository userRepository; + private final AuthProviderAccountRepository authProviderAccountRepository; // application.yml 적용 필요 @Value("${spring.mail.username}") @@ -34,19 +43,36 @@ public class EmailService { // 인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { if (userRepository.existsByEmail(toEmail)) { // 이미 해당 이메일로 생성한 계정이 있으면 - throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); // 이메일 중복 예외(회원가입 시 사용했던 예외) + //해당 이메일 값을 가진 AuthProviderAccount List 로 조회 + List authProviderAccounts = authProviderAccountRepository.findByUserEmail(toEmail); + + //만약 AuthProviderAccount 가 없으면 + if (authProviderAccounts.isEmpty()) { + //단순 이메일 회원가입에서 이메일 값이 중복인 것이므로 예외(기존 예외처리 로직) + throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); + } else { //만약 AuthProviderAccount 가 있으면 + //해당 소셜 로그인 플랫폼 타입을 추출해서 반환 + List providers = new ArrayList<>(); + for (AuthProviderAccount authProviderAccount : authProviderAccounts) { + Provider provider = authProviderAccount.getProvider(); + providers.add(provider); + } + + return UserConverter.toEmailSentResponseFail(toEmail, providers); + } } + //해당 이메일로 이미 생성된 계정 없으면 이메일 전송 진행 String type = "회원가입"; - - return emailSendTemplate(toEmail, type); + //템플릿 호출 + return (EmailSentResponse) emailSendTemplate(toEmail, type); } // 비밀번호 재설정을 위한 인증코드 이메일 발송 로직 (이미 회원가입 된 상태에서 비밀번호 재설정) - public EmailSentResponse sendEmailForPwd(String toEmail) { + public PasswordResetResponse sendEmailForPwd(String toEmail) { if (userRepository.existsByEmail(toEmail)) { // 이미 회원가입 되어있는 것이 확인되면 String type = "비밀번호 재설정"; - return emailSendTemplate(toEmail, type); // 정상적으로 이메일 발송 + return (PasswordResetResponse) emailSendTemplate(toEmail, type); // 정상적으로 이메일 발송 } else { // 만약 회원가입 되어있지 않다면 throw new UserHandler(UserErrorCode.USER_NOT_FOUND); // 예외발생 } @@ -69,8 +95,9 @@ public void sendEmailForOrgInvitation(String token, String toEmail, String orgNa } } - // 기존 이메일 발송 로직 템플릿 화 - private EmailSentResponse emailSendTemplate(String toEmail, String type) { + //기존 이메일 발송 로직 템플릿 화 + //"회원가입 시 인증 이메일 발송" 과 "비밀번호 재설정 시 인증 이메일 발송" 에 대한 Response 분리 위해 반환값 Object로 변경 (fix/#39) + private Object emailSendTemplate(String toEmail, String type) { // 인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 String redisKey = "CODE:" + toEmail; @@ -111,7 +138,13 @@ private EmailSentResponse emailSendTemplate(String toEmail, String type) { // 테스트 계정의 인증은 서버 로그를 통해 인증코드를 얻어 입력. redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 3L); - return new EmailSentResponse("인증코드를 이메일로 전송했습니다.", toEmail, 180L); + if (type.equals("회원가입")) { //해당 템플릿 메서드를 "회원가입을 위한 이메일 인증" 에서 호출한 경우, + //해당 DTO 형식에 맞춰 반환 + return UserConverter.toEmailSentResponseSuccess(toEmail); + } else { //"비밀번호 재설정" 에서 호출한 경우, + //해당 DTO 형식에 맞춰 반환 + return UserConverter.toPasswordResetResponse(toEmail); + } } // 인증코드 검증 메서드 diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/repository/AuthProviderAccountRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/repository/AuthProviderAccountRepository.java index 43320b1..74d944f 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/repository/AuthProviderAccountRepository.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/repository/AuthProviderAccountRepository.java @@ -2,7 +2,14 @@ import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface AuthProviderAccountRepository extends JpaRepository { AuthProviderAccount findByProviderId(String username); + + @Query("select apa from AuthProviderAccount apa where apa.user.email = :email") + List findByUserEmail(@Param(value = "email") String email); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java index dec243b..e0f3e37 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java @@ -4,6 +4,7 @@ import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SmsRequest; import com.whereyouad.WhereYouAd.domains.user.application.dto.request.PwdResetRequest; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; +import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse; import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService; @@ -68,11 +69,11 @@ public ResponseEntity> verifySms( } @PostMapping("/password-reset/request") - public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) { - EmailSentResponse emailSentResponse = emailService.sendEmailForPwd(request.email()); + public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) { + PasswordResetResponse response = emailService.sendEmailForPwd(request.email()); return ResponseEntity.ok( - DataResponse.from(emailSentResponse) + DataResponse.from(response) ); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/docs/UserControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/docs/UserControllerDocs.java index d879959..e5d7494 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/docs/UserControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/docs/UserControllerDocs.java @@ -6,6 +6,7 @@ import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SignUpRequest; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse; +import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse; import com.whereyouad.WhereYouAd.global.response.DataResponse; @@ -32,7 +33,8 @@ public interface UserControllerDocs { @Operation( summary = "이메일 인증코드 전송 API", description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" + - "테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다." + "테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다.\n\n" + + "이미 소셜 로그인으로 가입된 이메일 값이 요청으로 들어올 경우, isProviderLinked = true, providerType = [KAKAO, NAVER...] 와 같이 값이 나오며 이메일은 전송되지 않습니다." ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"), @@ -59,7 +61,7 @@ public interface UserControllerDocs { @ApiResponse(responseCode = "400_2", description = "이메일 전송실패(이메일 오타 등)"), @ApiResponse(responseCode = "404_1", description = "해당 이메일로 가입한 회원 존재하지 않음") }) - public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request); + public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request); @Operation( summary = "사용자 비밀번호 재설정 API",