From a32114f3f78e7fab5277474ba7a7a22be789bd05 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Sat, 21 Feb 2026 21:41:29 +0900 Subject: [PATCH 01/12] =?UTF-8?q?:sparkles:=20feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=EB=B6=84=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20EmailSentResponse=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20PasswordResetResponse=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/application/dto/response/EmailSentResponse.java | 6 +++++- .../application/dto/response/PasswordResetResponse.java | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/response/PasswordResetResponse.java 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..6c847c9 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,12 @@ package com.whereyouad.WhereYouAd.domains.user.application.dto.response; +import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; + public record EmailSentResponse( String message, //인증코드를 이메일로 전송했습니다. String email, //전송한 이메일 - long expireIn //만료시간 (500L -> 500초) + Long expireIn, //만료시간 (500L -> 500초) + boolean isProviderLinked, + Provider providerType ) { } 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초) +) { +} From 7c22bc05a0a3eedb7efe08de215544a97d5a9bfb Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Sat, 21 Feb 2026 21:41:49 +0900 Subject: [PATCH 02/12] =?UTF-8?q?:sparkles:=20feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=EB=B6=84=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20AuthProviderAccountRepository=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/AuthProviderAccountRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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..11f37b0 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 @@ -1,8 +1,16 @@ package com.whereyouad.WhereYouAd.domains.user.persistence.repository; import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount; +import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User; +import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; public interface AuthProviderAccountRepository extends JpaRepository { AuthProviderAccount findByProviderId(String username); + + @Query("select apa from AuthProviderAccount apa where apa.user = :user") + Optional findByUser(@Param(value = "user") User user); } From 0d48647686cb4fa2c628672a08fd020f10f87c8d Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Sat, 21 Feb 2026 21:42:11 +0900 Subject: [PATCH 03/12] =?UTF-8?q?:sparkles:=20feat:=20UserConverter=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/UserConverter.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 197f017..be56152 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,6 +1,9 @@ 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.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; @@ -32,4 +35,27 @@ public static OAuth2UserInfo toOAuth2UserInfo(User user, OAuth2Response oAuth2Re .provider(oAuth2Response.getProvider()) .build(); } + + public static EmailSentResponse toEmailSentResponseSuccess(String email) { + return new EmailSentResponse("인증 코드를 이메일로 전송했습니다", + email, + 180L, + false, + null); + } + + public static EmailSentResponse toEmailSentResponseFail(String email, Provider provider) { + return new EmailSentResponse("이미 소셜 계정으로 가입된 이메일 입니다.", + email, + null, + true, + provider); + } + + public static PasswordResetResponse toPasswordResetResponse(String email) { + + return new PasswordResetResponse("인증 코드를 이메일로 전송했습니다.", + email, + 180L); + } } From dfc06d470f84151caf9076ae2e7ffe5ed55ee4b0 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Sat, 21 Feb 2026 21:43:37 +0900 Subject: [PATCH 04/12] =?UTF-8?q?:bug:=20fix:=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=EB=A7=8C=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EB=90=9C=20=EC=83=81=ED=83=9C=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EC=95=8C=EB=A0=A4?= =?UTF-8?q?=EC=A3=BC=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/service/EmailService.java | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) 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..06e376b 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,14 @@ 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.entity.User; +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 +20,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + @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,32 @@ public class EmailService { // 인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { if (userRepository.existsByEmail(toEmail)) { // 이미 해당 이메일로 생성한 계정이 있으면 - throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); // 이메일 중복 예외(회원가입 시 사용했던 예외) +// throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); // 이메일 중복 예외(회원가입 시 사용했던 예외) + + User user = userRepository.findUserByEmail(toEmail) + .orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND)); + + Optional authProviderAccount = authProviderAccountRepository.findByUser(user); + + if (authProviderAccount.isEmpty()) { + throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); + } else { + Provider provider = authProviderAccount.get().getProvider(); + + return UserConverter.toEmailSentResponseFail(toEmail, provider); + } } 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); // 예외발생 } @@ -70,7 +92,7 @@ public void sendEmailForOrgInvitation(String token, String toEmail, String orgNa } // 기존 이메일 발송 로직 템플릿 화 - private EmailSentResponse emailSendTemplate(String toEmail, String type) { + private Object emailSendTemplate(String toEmail, String type) { // 인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 String redisKey = "CODE:" + toEmail; @@ -111,7 +133,12 @@ private EmailSentResponse emailSendTemplate(String toEmail, String type) { // 테스트 계정의 인증은 서버 로그를 통해 인증코드를 얻어 입력. redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 3L); - return new EmailSentResponse("인증코드를 이메일로 전송했습니다.", toEmail, 180L); + if (type.equals("회원가입")) { + return UserConverter.toEmailSentResponseSuccess(toEmail); + } else { + return UserConverter.toPasswordResetResponse(toEmail); + } +// return UserConverter.toEmailSentResponseSuccess(toEmail); } // 인증코드 검증 메서드 From 5269defbee7ad32945923107a84be2e7c054d15f Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Sat, 21 Feb 2026 21:44:08 +0900 Subject: [PATCH 05/12] =?UTF-8?q?:bug:=20fix:=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=EC=9A=A9=20=EC=9D=B8=EC=A6=9D=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=84=EC=86=A1=20API,=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=EC=9A=A9=20API?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/user/presentation/UserController.java | 7 ++++--- .../domains/user/presentation/docs/UserControllerDocs.java | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) 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 729653e..85667df 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.SmsResponse; import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService; import com.whereyouad.WhereYouAd.domains.user.domain.service.SmsService; @@ -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 ce462ed..0dbc64f 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 @@ -5,6 +5,7 @@ import com.whereyouad.WhereYouAd.domains.user.application.dto.request.PwdResetRequest; 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.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; @@ -56,7 +57,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", From 63a5867d10743c7e40d86a38bfaa61aad69173b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 17:38:28 +0900 Subject: [PATCH 06/12] =?UTF-8?q?:art:=20style:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/service/EmailService.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) 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 06e376b..21d50df 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 @@ -43,24 +43,28 @@ public class EmailService { // 인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { if (userRepository.existsByEmail(toEmail)) { // 이미 해당 이메일로 생성한 계정이 있으면 -// throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); // 이메일 중복 예외(회원가입 시 사용했던 예외) - + //해당 사용자 정보 조회 User user = userRepository.findUserByEmail(toEmail) .orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND)); + //해당 사용자에 연관된 AuthProviderAccount 조회 Optional authProviderAccount = authProviderAccountRepository.findByUser(user); + //만약 AuthProviderAccount 가 없으면 if (authProviderAccount.isEmpty()) { + //단순 이메일 회원가입에서 이메일 값이 중복인 것이므로 예외(기존 예외처리 로직) throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); - } else { + } else { //만약 AuthProviderAccount 가 있으면 + //해당 소셜 로그인 플랫폼 타입을 추출해서 반환 Provider provider = authProviderAccount.get().getProvider(); return UserConverter.toEmailSentResponseFail(toEmail, provider); } } + //해당 이메일로 이미 생성된 계정 없으면 이메일 전송 진행 String type = "회원가입"; - + //템플릿 호출 return (EmailSentResponse) emailSendTemplate(toEmail, type); } @@ -91,7 +95,8 @@ public void sendEmailForOrgInvitation(String token, String toEmail, String orgNa } } - // 기존 이메일 발송 로직 템플릿 화 + //기존 이메일 발송 로직 템플릿 화 + //"회원가입 시 인증 이메일 발송" 과 "비밀번호 재설정 시 인증 이메일 발송" 에 대한 Response 분리 위해 반환값 Object로 변경 (fix/#39) private Object emailSendTemplate(String toEmail, String type) { // 인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 @@ -133,12 +138,13 @@ private Object emailSendTemplate(String toEmail, String type) { // 테스트 계정의 인증은 서버 로그를 통해 인증코드를 얻어 입력. redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 3L); - if (type.equals("회원가입")) { + if (type.equals("회원가입")) { //해당 템플릿 메서드를 "회원가입을 위한 이메일 인증" 에서 호출한 경우, + //해당 DTO 형식에 맞춰 반환 return UserConverter.toEmailSentResponseSuccess(toEmail); - } else { + } else { //"비밀번호 재설정" 에서 호출한 경우, + //해당 DTO 형식에 맞춰 반환 return UserConverter.toPasswordResetResponse(toEmail); } -// return UserConverter.toEmailSentResponseSuccess(toEmail); } // 인증코드 검증 메서드 From e074e263210bb0626e1e090b2ded905f865e9ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 17:38:38 +0900 Subject: [PATCH 07/12] =?UTF-8?q?:art:=20style:=20Swagger=20Docs=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/user/presentation/docs/UserControllerDocs.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 0dbc64f..9e32dde 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 @@ -30,7 +30,8 @@ public interface UserControllerDocs { @Operation( summary = "이메일 인증코드 전송 API", description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" + - "테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다." + "테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다.\n\n" + + "이미 소셜 로그인으로 가입된 이메일 값이 요청으로 들어올 경우, isProviderLinked = true, providerType = KAKAO 와 같이 값이 나오며 이메일은 전송되지 않습니다." ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"), From 62ec197bcb1b23c9ecf19a97e7bbad10663b4f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 17:55:29 +0900 Subject: [PATCH 08/12] =?UTF-8?q?:bug:=20fix:=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20imp?= =?UTF-8?q?ort=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/repository/AuthProviderAccountRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 11f37b0..812a6eb 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,9 +2,9 @@ import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount; import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User; -import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.Optional; From 1ce5347a83239e5de2c3cbcf70c9f93ddabcd6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 22:53:55 +0900 Subject: [PATCH 09/12] =?UTF-8?q?:bug:=20fix:=20EmailSentResponse=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Provider=20=EB=A5=BC=20List=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20Converter=20=EC=97=90=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/application/dto/response/EmailSentResponse.java | 4 +++- .../domains/user/application/mapper/UserConverter.java | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) 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 6c847c9..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 @@ -2,11 +2,13 @@ import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; +import java.util.List; + public record EmailSentResponse( String message, //인증코드를 이메일로 전송했습니다. String email, //전송한 이메일 Long expireIn, //만료시간 (500L -> 500초) boolean isProviderLinked, - Provider providerType + List providerTypes ) { } 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 be56152..4d223d3 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 @@ -9,6 +9,8 @@ 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) { @@ -44,12 +46,12 @@ public static EmailSentResponse toEmailSentResponseSuccess(String email) { null); } - public static EmailSentResponse toEmailSentResponseFail(String email, Provider provider) { + public static EmailSentResponse toEmailSentResponseFail(String email, List providers) { return new EmailSentResponse("이미 소셜 계정으로 가입된 이메일 입니다.", email, null, true, - provider); + providers); } public static PasswordResetResponse toPasswordResetResponse(String email) { From 43e0edb4df48dc963c0600e41382e9e4934f0e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 22:55:16 +0900 Subject: [PATCH 10/12] =?UTF-8?q?:bug:=20fix:=20AuthProviderAccountReposit?= =?UTF-8?q?ory=20=EC=97=90=EC=84=9C=20User=20=EB=A1=9C=20AuthProviderAccou?= =?UTF-8?q?nt=20=EC=A1=B0=ED=9A=8C=20->=20"User=20=EC=9D=98=20email=20?= =?UTF-8?q?=EB=A1=9C=20=EC=A1=B0=ED=9A=8C"=EB=A1=9C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/AuthProviderAccountRepository.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 812a6eb..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 @@ -1,16 +1,15 @@ package com.whereyouad.WhereYouAd.domains.user.persistence.repository; import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount; -import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.Optional; +import java.util.List; public interface AuthProviderAccountRepository extends JpaRepository { AuthProviderAccount findByProviderId(String username); - @Query("select apa from AuthProviderAccount apa where apa.user = :user") - Optional findByUser(@Param(value = "user") User user); + @Query("select apa from AuthProviderAccount apa where apa.user.email = :email") + List findByUserEmail(@Param(value = "email") String email); } From ba42c5c7229ae9d9863dbe5cea22e654970d6a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Mon, 23 Feb 2026 22:55:53 +0900 Subject: [PATCH 11/12] =?UTF-8?q?:bug:=20fix:=20Provider=20=EB=A5=BC=20?= =?UTF-8?q?=EC=97=AC=EB=9F=AC=EA=B0=9C=20=EC=B6=94=EC=B6=9C=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/service/EmailService.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 21d50df..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 @@ -7,7 +7,6 @@ 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.entity.User; 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; @@ -20,7 +19,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; @Transactional @Service @@ -43,22 +43,22 @@ public class EmailService { // 인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { if (userRepository.existsByEmail(toEmail)) { // 이미 해당 이메일로 생성한 계정이 있으면 - //해당 사용자 정보 조회 - User user = userRepository.findUserByEmail(toEmail) - .orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND)); - - //해당 사용자에 연관된 AuthProviderAccount 조회 - Optional authProviderAccount = authProviderAccountRepository.findByUser(user); + //해당 이메일 값을 가진 AuthProviderAccount List 로 조회 + List authProviderAccounts = authProviderAccountRepository.findByUserEmail(toEmail); //만약 AuthProviderAccount 가 없으면 - if (authProviderAccount.isEmpty()) { + if (authProviderAccounts.isEmpty()) { //단순 이메일 회원가입에서 이메일 값이 중복인 것이므로 예외(기존 예외처리 로직) throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); } else { //만약 AuthProviderAccount 가 있으면 //해당 소셜 로그인 플랫폼 타입을 추출해서 반환 - Provider provider = authProviderAccount.get().getProvider(); + List providers = new ArrayList<>(); + for (AuthProviderAccount authProviderAccount : authProviderAccounts) { + Provider provider = authProviderAccount.getProvider(); + providers.add(provider); + } - return UserConverter.toEmailSentResponseFail(toEmail, provider); + return UserConverter.toEmailSentResponseFail(toEmail, providers); } } From 2de0ce7bdf6c5f91089de450f126eafe7665b804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Tue, 24 Feb 2026 19:33:28 +0900 Subject: [PATCH 12/12] =?UTF-8?q?:art:=20style:=20UserController=20Swagger?= =?UTF-8?q?=20Docs=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/user/presentation/docs/UserControllerDocs.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 beab04c..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 @@ -34,7 +34,7 @@ public interface UserControllerDocs { summary = "이메일 인증코드 전송 API", description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" + "테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다.\n\n" + - "이미 소셜 로그인으로 가입된 이메일 값이 요청으로 들어올 경우, isProviderLinked = true, providerType = KAKAO 와 같이 값이 나오며 이메일은 전송되지 않습니다." + "이미 소셜 로그인으로 가입된 이메일 값이 요청으로 들어올 경우, isProviderLinked = true, providerType = [KAKAO, NAVER...] 와 같이 값이 나오며 이메일은 전송되지 않습니다." ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"),