From c8a5bf20d8ed6c75039836eb7324787558be0121 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Thu, 29 Jan 2026 13:07:04 +0900 Subject: [PATCH 01/16] =?UTF-8?q?:sparkles:=20feat:=20User=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WhereYouAd/domains/user/persistence/entity/User.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java index 3b5fb0b..adb782f 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java @@ -42,4 +42,8 @@ public class User extends BaseEntity { @Column(nullable = false, name = "status") @ColumnDefault("'ACTIVE'") //기본값 ACTIVE private UserStatus status; //ACTIVE, SUSPENDED, DELETED + + public void resetPassword(String newPassword) { + this.password = newPassword; + } } From f9926d97f4bf9089ea0df0a6ac22d601e1698277 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Thu, 29 Jan 2026 13:08:05 +0900 Subject: [PATCH 02/16] =?UTF-8?q?:sparkles:=20feat:=20UserSignUpException?= =?UTF-8?q?=20->=20UserException=20=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD,=20UserErrorCode=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BD=94=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 --- .../{UserSignUpException.java => UserException.java} | 4 ++-- .../WhereYouAd/domains/user/exception/code/UserErrorCode.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) rename src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/{UserSignUpException.java => UserException.java} (66%) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserSignUpException.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java similarity index 66% rename from src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserSignUpException.java rename to src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java index 6042838..c557a16 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserSignUpException.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java @@ -3,8 +3,8 @@ import com.whereyouad.WhereYouAd.global.exception.AppException; import com.whereyouad.WhereYouAd.global.exception.BaseErrorCode; -public class UserSignUpException extends AppException { - public UserSignUpException(BaseErrorCode errorCode) { +public class UserException extends AppException { + public UserException(BaseErrorCode errorCode) { super(errorCode); } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java index 50a7027..512d319 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java @@ -11,6 +11,8 @@ public enum UserErrorCode implements BaseErrorCode { USER_EMAIL_DUPLICATE(HttpStatus.BAD_REQUEST, "USER_400_1", "이미 사용중인 이메일 입니다."), USER_EMAIL_NOT_VALID(HttpStatus.BAD_REQUEST, "USER_400_2", "해당 이메일로 메일 전송에 실패했습니다."), USER_EMAIL_AUTH_INVALID(HttpStatus.BAD_REQUEST, "USER_400_3", "인증 코드가 올바르지 않습니다."), + USER_PASSWORD_SAME_AS_OLD(HttpStatus.BAD_REQUEST, "USER_400_4", "기존 비밀번호와 동일한 비밀번호로는 변경 불가합니다."), + EMAIL_USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_404_1", "해당 이메일로 가입한 회원이 존재하지 않습니다."), USER_EMAIL_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "USER_401_1", "이메일 인증이 진행되지 않았습니다."), ; From 94345d4b210c1a48c66324a2a84e62295bf91a4c Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Thu, 29 Jan 2026 13:08:49 +0900 Subject: [PATCH 03/16] =?UTF-8?q?:sparkles:=20feat:=20EmailService=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=EA=B0=80=EC=9E=85=EC=8B=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=9D=B8=EC=A6=9D=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=ED=85=9C=ED=94=8C=EB=A6=BF=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=ED=99=94=20&=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20=EC=9E=AC=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=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 --- .../user/domain/service/EmailService.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 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 abb31ce..9ef0554 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,7 +1,7 @@ package com.whereyouad.WhereYouAd.domains.user.domain.service; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; -import com.whereyouad.WhereYouAd.domains.user.exception.UserSignUpException; +import com.whereyouad.WhereYouAd.domains.user.exception.UserException; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository; import com.whereyouad.WhereYouAd.global.utils.RedisUtil; @@ -31,8 +31,21 @@ public class EmailService { //인증코드 이메일 발송 로직 public EmailSentResponse sendEmail(String toEmail) { + return emailSendTemplate(toEmail); + } + + public EmailSentResponse sendEmailForPwd(String toEmail) { + if (userRepository.existsByEmail(toEmail)) { + return emailSendTemplate(toEmail); + } else { + throw new UserException(UserErrorCode.EMAIL_USER_NOT_FOUND); + } + } + + //기존 이메일 발송 로직 템플릿 화 + private EmailSentResponse emailSendTemplate(String toEmail) { if (userRepository.existsByEmail(toEmail)) { //이미 해당 이메일로 생성한 계정이 있으면 - throw new UserSignUpException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) + throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) } //인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 @@ -64,7 +77,7 @@ public EmailSentResponse sendEmail(String toEmail) { emailSender.send(message); //만약 실제 존재하는 이메일인데 사용자가 오타를 냈다면 } catch (MailException e) { //예외 발생 - throw new UserSignUpException(UserErrorCode.USER_EMAIL_NOT_VALID); //통합 응답 처리 예외로 반환 + throw new UserException(UserErrorCode.USER_EMAIL_NOT_VALID); //통합 응답 처리 예외로 반환 } } @@ -85,7 +98,7 @@ public void verifyEmailCode(String email, String inputCode) { //만약 인증코드가 없거나 잘못 입력했다면, if (savedCode == null || !savedCode.equals(inputCode)) { - throw new UserSignUpException(UserErrorCode.USER_EMAIL_AUTH_INVALID); //예외 발생(BAD_REQUEST) + throw new UserException(UserErrorCode.USER_EMAIL_AUTH_INVALID); //예외 발생(BAD_REQUEST) } //정상적으로 인증코드를 입력했다면, From e5be6f8bf7b2741c73ebae86c76a7563e8815e31 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Thu, 29 Jan 2026 13:09:21 +0900 Subject: [PATCH 04/16] =?UTF-8?q?:sparkles:=20feat:=20UserService=20?= =?UTF-8?q?=EC=8B=A4=EC=A0=9C=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=84=A4=EC=A0=95=20=EB=A9=94=EC=84=9C=EB=93=9C=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/domain/service/UserService.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java index c347422..a7db71e 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java @@ -1,6 +1,6 @@ package com.whereyouad.WhereYouAd.domains.user.domain.service; -import com.whereyouad.WhereYouAd.domains.user.exception.UserSignUpException; +import com.whereyouad.WhereYouAd.domains.user.exception.UserException; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus; import com.whereyouad.WhereYouAd.domains.user.application.mapper.UserConverter; @@ -26,7 +26,7 @@ public class UserService { //회원가입 메서드 public SignUpResponse signUpUser(SignUpRequest request) { if (userRepository.existsByEmail(request.email())) { //이미 이메일로 만든 계정이 존재할 시 - throw new UserSignUpException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외 + throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외 } //추가 : 이메일 인증되었는지 확인 -> 악의적 공격자가 이메일 인증을 건너뛰고 회원가입 URL 등으로 바로 들어왔을 경우 @@ -35,7 +35,7 @@ public SignUpResponse signUpUser(SignUpRequest request) { //인증이 안되었다면, if (isEmailVerified == null || !isEmailVerified.equals("TRUE")) { - throw new UserSignUpException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생(UNAUTHORIZED) + throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생(UNAUTHORIZED) } //비밀번호 암호화 -> SecurityConfig 클래스 내 에서 BCryptPasswordEncoder 를 Bean 등록한거로 사용 @@ -57,4 +57,30 @@ public SignUpResponse signUpUser(SignUpRequest request) { //Response DTO 로 변환 및 반환 return UserConverter.toSignInResponse(savedUser); } + + public void passwordReset(String email, String password) { + String isEmailVerified = redisUtil.getData("VERIFIED:" + email); + + //인증이 안되었다면, + if (isEmailVerified == null || !isEmailVerified.equals("TRUE")) { + throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생 + } + + //기존 비밀번호와 새 비밀번호가 일치할 시 예외 발생 + String newPassword = passwordEncoder.encode(password); + + User user = userRepository.findUserByEmail(email) + .orElseThrow(() -> new UserException(UserErrorCode.EMAIL_USER_NOT_FOUND)); + + String oldPassword = user.getPassword(); + + if (passwordEncoder.matches(newPassword, oldPassword)) { //이전 비밀번호 == 새로운 비밀번호이면 + throw new UserException(UserErrorCode.USER_PASSWORD_SAME_AS_OLD); //예외 발생 + } + + //비밀번호 변경 (JPA Dirty Checking) + user.resetPassword(newPassword); + + redisUtil.deleteData("VERIFIED:" + email); + } } From 4025e6df1b1d1ea60be2c399095b2f10789ec7c1 Mon Sep 17 00:00:00 2001 From: ojy0903 Date: Thu, 29 Jan 2026 13:10:10 +0900 Subject: [PATCH 05/16] =?UTF-8?q?:sparkles:=20feat:=20UserController=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EC=9D=B8=EC=A6=9D=20=EC=A0=84=EC=86=A1=20API,=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20API=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20Request=20DT?= =?UTF-8?q?O=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/PwdResetRequest.java | 13 +++++++++++++ .../user/presentation/UserController.java | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/request/PwdResetRequest.java diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/request/PwdResetRequest.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/request/PwdResetRequest.java new file mode 100644 index 0000000..73ad85c --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/request/PwdResetRequest.java @@ -0,0 +1,13 @@ +package com.whereyouad.WhereYouAd.domains.user.application.dto.request; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record PwdResetRequest( + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "이메일 형식이 올바르지 않습니다.") + String email, + @NotBlank(message = "비밀번호는 필수입니다.") + String password +) { +} 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 b0c6d39..a765ce9 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 @@ -1,6 +1,7 @@ package com.whereyouad.WhereYouAd.domains.user.presentation; import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest; +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.domain.service.EmailService; import com.whereyouad.WhereYouAd.domains.user.domain.service.UserService; @@ -48,4 +49,22 @@ public ResponseEntity> verifyEmail(@RequestBody @Valid Emai DataResponse.from("이메일 인증이 성공적으로 완료되었습니다.") ); } + + @PostMapping("/pwd-reset/email") + public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) { + EmailSentResponse emailSentResponse = emailService.sendEmailForPwd(request.email()); + + return ResponseEntity.ok( + DataResponse.created(emailSentResponse) + ); + } + + @PostMapping("/pwd-reset") + public ResponseEntity> resetPassword(@RequestBody @Valid PwdResetRequest request) { + userService.passwordReset(request.email(), request.password()); + + return ResponseEntity.ok( + DataResponse.from("비밀번호 변경이 완료되었습니다.") + ); + } } From 07e73402a01b1d7f67f80ff4fbc34e4fb4abfd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 19:58:54 +0900 Subject: [PATCH 06/16] =?UTF-8?q?:bug:=20fix:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20PR=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20UserSignUpException=20->=20UserException?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WhereYouAd/domains/user/domain/constant/Provider.java | 4 ++-- .../security/oauth2/service/CustomOAuth2UserService.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java index bf84f27..f24c296 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java @@ -1,6 +1,6 @@ package com.whereyouad.WhereYouAd.domains.user.domain.constant; -import com.whereyouad.WhereYouAd.domains.user.exception.UserSignUpException; +import com.whereyouad.WhereYouAd.domains.user.exception.UserException; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -22,6 +22,6 @@ public static Provider fromRegistrationId(String registrationId) { return Arrays.stream(Provider.values()) .filter(socialType -> socialType.getRegistrationId().equals(registrationId)) .findFirst() - .orElseThrow(() -> new UserSignUpException(UserErrorCode.NOT_PROVIDE_SOCIAL)); + .orElseThrow(() -> new UserException(UserErrorCode.NOT_PROVIDE_SOCIAL)); } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java b/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java index 0cbd2bd..ca8f2ff 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java @@ -1,8 +1,8 @@ package com.whereyouad.WhereYouAd.global.security.oauth2.service; +import com.whereyouad.WhereYouAd.domains.user.exception.UserException; import com.whereyouad.WhereYouAd.global.security.oauth2.dto.*; import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; -import com.whereyouad.WhereYouAd.domains.user.exception.UserSignUpException; 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; @@ -50,7 +50,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic } else if (provider == Provider.KAKAO){ oAuth2Response = new KaKaoResponse(oAuth2User.getAttributes()); } else { - throw new UserSignUpException(UserErrorCode.NOT_PROVIDE_SOCIAL); + throw new UserException(UserErrorCode.NOT_PROVIDE_SOCIAL); } // 사용자 소셜 로그인 고유 id (제공자 + 소셜 발급 id) -> (중복 회원 가입 방지) @@ -69,7 +69,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic user = userOptional.get(); // 기존 이메일에 해당하는 유저가 존재하지만 이메일 인증이 안된 경우 -> 연동 불가 if (!user.isEmailVerified()) { - throw new UserSignUpException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); + throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); } } // 신규 유저(기존 email X, 소셜 로그인 처음) -> DB에 저장 From 715964724111bf7bc8c230c5d5673d5459f7008c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 19:59:27 +0900 Subject: [PATCH 07/16] =?UTF-8?q?:sparkles:=20feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20PR=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20User=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=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 --- .../WhereYouAd/domains/user/persistence/entity/User.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java index 3407afb..33aab14 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java @@ -47,4 +47,9 @@ public class User extends BaseEntity { public void updateProfile(String name){ this.name = name; } + + + public void resetPassword(String password) { + this.password = password; + } } From c8867ad51daa798a36813c2c14fe84c497626d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 20:01:04 +0900 Subject: [PATCH 08/16] =?UTF-8?q?:bug:=20fix:=20EmailService=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=A0=84=EC=86=A1=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/service/EmailService.java | 17 +++++++++-------- 1 file changed, 9 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 9ef0554..d3b97a5 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 @@ -28,25 +28,26 @@ public class EmailService { @Value("${spring.mail.username}") private String senderEmail; - //인증코드 이메일 발송 로직 + //인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { + if (userRepository.existsByEmail(toEmail)) { //이미 해당 이메일로 생성한 계정이 있으면 + throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) + } return emailSendTemplate(toEmail); } + //비밀번호 재설정을 위한 인증코드 이메일 발송 로직 (이미 회원가입 된 상태에서 비밀번호 재설정) public EmailSentResponse sendEmailForPwd(String toEmail) { - if (userRepository.existsByEmail(toEmail)) { - return emailSendTemplate(toEmail); - } else { - throw new UserException(UserErrorCode.EMAIL_USER_NOT_FOUND); + if (!userRepository.existsByEmail(toEmail)) { //이미 회원가입 되어있는 것이 확인되면 + return emailSendTemplate(toEmail); //정상적으로 이메일 발송 + } else { //만약 회원가입 되어있지 않다면 + throw new UserException(UserErrorCode.USER_NOT_FOUND); //예외발생 } } //기존 이메일 발송 로직 템플릿 화 private EmailSentResponse emailSendTemplate(String toEmail) { - if (userRepository.existsByEmail(toEmail)) { //이미 해당 이메일로 생성한 계정이 있으면 - throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) - } //인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 String redisKey = "CODE:" + toEmail; From f4712c4f72f1c106c559fce0211821f9cf111161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 20:01:50 +0900 Subject: [PATCH 09/16] =?UTF-8?q?:sparkles:=20feat:=20UserErrorCode=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BD=94=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 --- .../WhereYouAd/domains/user/exception/code/UserErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java index 260cf86..2ac2870 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/code/UserErrorCode.java @@ -13,6 +13,7 @@ public enum UserErrorCode implements BaseErrorCode { USER_EMAIL_NOT_VALID(HttpStatus.BAD_REQUEST, "USER_400_2", "해당 이메일로 메일 전송에 실패했습니다."), USER_EMAIL_AUTH_INVALID(HttpStatus.BAD_REQUEST, "USER_400_3", "인증 코드가 올바르지 않습니다."), NOT_PROVIDE_SOCIAL(HttpStatus.BAD_REQUEST, "USER_400_4", "지원하지 않는 소셜 로그인 방식입니다."), + USER_PASSWORD_SAME_AS_OLD(HttpStatus.BAD_REQUEST, "USER_400_5", "이전 비밀번호와 동일한 비밀번호로 바꿀 수 없습니다."), // 401 USER_EMAIL_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "USER_401_1", "이메일 인증이 진행되지 않았습니다."), From 3897a16350bb10d3070954f801ff0b5fa19f4bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 20:02:51 +0900 Subject: [PATCH 10/16] =?UTF-8?q?:sparkles:=20feat:=20UserService=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20UserSignUpException=20->=20UserException?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20&=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=B4?= =?UTF-8?q?=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/user/domain/service/UserService.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java index a7db71e..7dcfbee 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java @@ -58,7 +58,9 @@ public SignUpResponse signUpUser(SignUpRequest request) { return UserConverter.toSignInResponse(savedUser); } + //이미 회원가입 된 회원의 비밀번호 재설정 메서드 public void passwordReset(String email, String password) { + //이메일 인증이 되어있는지 확인 String isEmailVerified = redisUtil.getData("VERIFIED:" + email); //인증이 안되었다면, @@ -67,17 +69,17 @@ public void passwordReset(String email, String password) { } //기존 비밀번호와 새 비밀번호가 일치할 시 예외 발생 - String newPassword = passwordEncoder.encode(password); - User user = userRepository.findUserByEmail(email) - .orElseThrow(() -> new UserException(UserErrorCode.EMAIL_USER_NOT_FOUND)); + .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND)); String oldPassword = user.getPassword(); - if (passwordEncoder.matches(newPassword, oldPassword)) { //이전 비밀번호 == 새로운 비밀번호이면 + if (passwordEncoder.matches(password, oldPassword)) { //새로운 비밀번호 == 이전 비밀번호이면 throw new UserException(UserErrorCode.USER_PASSWORD_SAME_AS_OLD); //예외 발생 } + //새 비밀번호 암호화 & 저장 + String newPassword = passwordEncoder.encode(password); //비밀번호 변경 (JPA Dirty Checking) user.resetPassword(newPassword); From 01e2820266b118d16c1175a93a89f675b428805b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 20:04:31 +0900 Subject: [PATCH 11/16] =?UTF-8?q?:sparkles:=20feat:=20UserController=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=84=A4=EC=A0=95=20=EA=B4=80=EB=A0=A8=20API=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95=20+=20Swagger=20Docs=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/presentation/UserController.java | 4 +-- .../presentation/docs/UserControllerDocs.java | 30 +++++++++++++++++-- 2 files changed, 29 insertions(+), 5 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 a765ce9..0ba1032 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 @@ -50,7 +50,7 @@ public ResponseEntity> verifyEmail(@RequestBody @Valid Emai ); } - @PostMapping("/pwd-reset/email") + @PostMapping("/password-reset/request") public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) { EmailSentResponse emailSentResponse = emailService.sendEmailForPwd(request.email()); @@ -59,7 +59,7 @@ public ResponseEntity> sendEmailForPwdReset(@Req ); } - @PostMapping("/pwd-reset") + @PostMapping("/password-reset/confirm") public ResponseEntity> resetPassword(@RequestBody @Valid PwdResetRequest request) { userService.passwordReset(request.email(), request.password()); 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 9048747..2eca79f 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 @@ -1,6 +1,7 @@ package com.whereyouad.WhereYouAd.domains.user.presentation.docs; import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest; +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.SignUpResponse; @@ -19,7 +20,7 @@ public interface UserControllerDocs { ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"), - @ApiResponse(responseCode = "400_2", description = "이메일 중복 회원 존재") + @ApiResponse(responseCode = "400_1", description = "이메일 중복 회원 존재") }) public ResponseEntity> signUp(@RequestBody @Valid SignUpRequest request); @@ -30,7 +31,7 @@ public interface UserControllerDocs { ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"), - @ApiResponse(responseCode = "400_3", description = "이메일 전송실패(이메일 오타 등)") + @ApiResponse(responseCode = "400_2", description = "이메일 전송실패(이메일 오타 등)") }) public ResponseEntity> sendEmail(@RequestBody @Valid EmailRequest.Send request); @@ -40,7 +41,30 @@ public interface UserControllerDocs { ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "성공"), - @ApiResponse(responseCode = "400_4", description = "실패(인증코드 불일치)") + @ApiResponse(responseCode = "400_3", description = "실패(인증코드 불일치)") }) public ResponseEntity> verifyEmail(@RequestBody @Valid EmailRequest.Verify request); + + @Operation( + summary = "사용자 비밀번호 재설정을 위한 이메일 인증코드 전송 API", + description = "회원의 이메일을 입력받아 해당 이메일로 인증코드를 전송합니다(인증코드 검증은 기존 /email-verify 로 진행)" + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "400_2", description = "이메일 전송실패(이메일 오타 등)"), + @ApiResponse(responseCode = "404_1", description = "해당 이메일로 가입한 회원 존재하지 않음") + }) + public ResponseEntity> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request); + + @Operation( + summary = "사용자 비밀번호 재설정 API", + description = "회원의 이메일과 재설정할 비밀번호를 입력받아 비밀번호를 재설정(이전과 같은 비밀번호 일 경우 예외 발생)" + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "400_5", description = "이전 비밀번호와 동일 비밀번호로 변경 불가"), + @ApiResponse(responseCode = "401_1", description = "이메일 인증 진행되지 않음"), + @ApiResponse(responseCode = "404_1", description = "해당 이메일로 가입한 회원 존재하지 않음") + }) + public ResponseEntity> resetPassword(@RequestBody @Valid PwdResetRequest request); } From 6b606241a4ee9060d593abfe0c8c3f8e8e7e7594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 30 Jan 2026 15:10:03 +0900 Subject: [PATCH 12/16] =?UTF-8?q?:bug:=20fix:=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=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EB=82=B4=EB=B6=80=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WhereYouAd/domains/user/domain/service/EmailService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d3b97a5..9b81cd0 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 @@ -39,7 +39,7 @@ public EmailSentResponse sendEmail(String toEmail) { //비밀번호 재설정을 위한 인증코드 이메일 발송 로직 (이미 회원가입 된 상태에서 비밀번호 재설정) public EmailSentResponse sendEmailForPwd(String toEmail) { - if (!userRepository.existsByEmail(toEmail)) { //이미 회원가입 되어있는 것이 확인되면 + if (userRepository.existsByEmail(toEmail)) { //이미 회원가입 되어있는 것이 확인되면 return emailSendTemplate(toEmail); //정상적으로 이메일 발송 } else { //만약 회원가입 되어있지 않다면 throw new UserException(UserErrorCode.USER_NOT_FOUND); //예외발생 From 0254bfa2b7a28b71bebccd0572197ac307cffb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Sat, 31 Jan 2026 17:02:06 +0900 Subject: [PATCH 13/16] =?UTF-8?q?:recycle:=20refactor:=20UserException=20-?= =?UTF-8?q?>=20UserHandler=20=EB=A1=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/user/domain/constant/Provider.java | 4 ++-- .../domains/user/domain/service/EmailService.java | 8 ++++---- .../domains/user/domain/service/UserService.java | 12 ++++++------ .../{UserException.java => handler/UserHandler.java} | 6 +++--- .../oauth2/service/CustomOAuth2UserService.java | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) rename src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/{UserException.java => handler/UserHandler.java} (50%) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java index f24c296..9805402 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/Provider.java @@ -1,6 +1,6 @@ package com.whereyouad.WhereYouAd.domains.user.domain.constant; -import com.whereyouad.WhereYouAd.domains.user.exception.UserException; +import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -22,6 +22,6 @@ public static Provider fromRegistrationId(String registrationId) { return Arrays.stream(Provider.values()) .filter(socialType -> socialType.getRegistrationId().equals(registrationId)) .findFirst() - .orElseThrow(() -> new UserException(UserErrorCode.NOT_PROVIDE_SOCIAL)); + .orElseThrow(() -> new UserHandler(UserErrorCode.NOT_PROVIDE_SOCIAL)); } } 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 9b81cd0..f7e201a 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 @@ -31,7 +31,7 @@ public class EmailService { //인증코드 이메일 발송 로직 (최초 회원가입 시) public EmailSentResponse sendEmail(String toEmail) { if (userRepository.existsByEmail(toEmail)) { //이미 해당 이메일로 생성한 계정이 있으면 - throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) + throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) } return emailSendTemplate(toEmail); @@ -42,7 +42,7 @@ public EmailSentResponse sendEmailForPwd(String toEmail) { if (userRepository.existsByEmail(toEmail)) { //이미 회원가입 되어있는 것이 확인되면 return emailSendTemplate(toEmail); //정상적으로 이메일 발송 } else { //만약 회원가입 되어있지 않다면 - throw new UserException(UserErrorCode.USER_NOT_FOUND); //예외발생 + throw new UserHandler(UserErrorCode.USER_NOT_FOUND); //예외발생 } } @@ -78,7 +78,7 @@ private EmailSentResponse emailSendTemplate(String toEmail) { emailSender.send(message); //만약 실제 존재하는 이메일인데 사용자가 오타를 냈다면 } catch (MailException e) { //예외 발생 - throw new UserException(UserErrorCode.USER_EMAIL_NOT_VALID); //통합 응답 처리 예외로 반환 + throw new UserHandler(UserErrorCode.USER_EMAIL_NOT_VALID); //통합 응답 처리 예외로 반환 } } @@ -99,7 +99,7 @@ public void verifyEmailCode(String email, String inputCode) { //만약 인증코드가 없거나 잘못 입력했다면, if (savedCode == null || !savedCode.equals(inputCode)) { - throw new UserException(UserErrorCode.USER_EMAIL_AUTH_INVALID); //예외 발생(BAD_REQUEST) + throw new UserHandler(UserErrorCode.USER_EMAIL_AUTH_INVALID); //예외 발생(BAD_REQUEST) } //정상적으로 인증코드를 입력했다면, diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java index 7dcfbee..c9fa01e 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java @@ -1,6 +1,6 @@ package com.whereyouad.WhereYouAd.domains.user.domain.service; -import com.whereyouad.WhereYouAd.domains.user.exception.UserException; +import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus; import com.whereyouad.WhereYouAd.domains.user.application.mapper.UserConverter; @@ -26,7 +26,7 @@ public class UserService { //회원가입 메서드 public SignUpResponse signUpUser(SignUpRequest request) { if (userRepository.existsByEmail(request.email())) { //이미 이메일로 만든 계정이 존재할 시 - throw new UserException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외 + throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외 } //추가 : 이메일 인증되었는지 확인 -> 악의적 공격자가 이메일 인증을 건너뛰고 회원가입 URL 등으로 바로 들어왔을 경우 @@ -35,7 +35,7 @@ public SignUpResponse signUpUser(SignUpRequest request) { //인증이 안되었다면, if (isEmailVerified == null || !isEmailVerified.equals("TRUE")) { - throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생(UNAUTHORIZED) + throw new UserHandler(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생(UNAUTHORIZED) } //비밀번호 암호화 -> SecurityConfig 클래스 내 에서 BCryptPasswordEncoder 를 Bean 등록한거로 사용 @@ -65,17 +65,17 @@ public void passwordReset(String email, String password) { //인증이 안되었다면, if (isEmailVerified == null || !isEmailVerified.equals("TRUE")) { - throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생 + throw new UserHandler(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //예외 발생 } //기존 비밀번호와 새 비밀번호가 일치할 시 예외 발생 User user = userRepository.findUserByEmail(email) - .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND)); + .orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND)); String oldPassword = user.getPassword(); if (passwordEncoder.matches(password, oldPassword)) { //새로운 비밀번호 == 이전 비밀번호이면 - throw new UserException(UserErrorCode.USER_PASSWORD_SAME_AS_OLD); //예외 발생 + throw new UserHandler(UserErrorCode.USER_PASSWORD_SAME_AS_OLD); //예외 발생 } //새 비밀번호 암호화 & 저장 diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/handler/UserHandler.java similarity index 50% rename from src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java rename to src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/handler/UserHandler.java index c557a16..e996f24 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/UserException.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/user/exception/handler/UserHandler.java @@ -1,10 +1,10 @@ -package com.whereyouad.WhereYouAd.domains.user.exception; +package com.whereyouad.WhereYouAd.domains.user.exception.handler; import com.whereyouad.WhereYouAd.global.exception.AppException; import com.whereyouad.WhereYouAd.global.exception.BaseErrorCode; -public class UserException extends AppException { - public UserException(BaseErrorCode errorCode) { +public class UserHandler extends AppException { + public UserHandler(BaseErrorCode errorCode) { super(errorCode); } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java b/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java index ca8f2ff..42e1386 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/global/security/oauth2/service/CustomOAuth2UserService.java @@ -1,6 +1,6 @@ package com.whereyouad.WhereYouAd.global.security.oauth2.service; -import com.whereyouad.WhereYouAd.domains.user.exception.UserException; +import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler; import com.whereyouad.WhereYouAd.global.security.oauth2.dto.*; import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider; import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode; @@ -50,7 +50,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic } else if (provider == Provider.KAKAO){ oAuth2Response = new KaKaoResponse(oAuth2User.getAttributes()); } else { - throw new UserException(UserErrorCode.NOT_PROVIDE_SOCIAL); + throw new UserHandler(UserErrorCode.NOT_PROVIDE_SOCIAL); } // 사용자 소셜 로그인 고유 id (제공자 + 소셜 발급 id) -> (중복 회원 가입 방지) @@ -69,7 +69,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic user = userOptional.get(); // 기존 이메일에 해당하는 유저가 존재하지만 이메일 인증이 안된 경우 -> 연동 불가 if (!user.isEmailVerified()) { - throw new UserException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); + throw new UserHandler(UserErrorCode.USER_EMAIL_NOT_VERIFIED); } } // 신규 유저(기존 email X, 소셜 로그인 처음) -> DB에 저장 From 5b1df9df097239f0c8a61002de35a9db3d5c86c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Sat, 31 Jan 2026 17:02:44 +0900 Subject: [PATCH 14/16] =?UTF-8?q?:recycle:=20refactor:=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20API?= =?UTF-8?q?=20response=20=EB=A5=BC=20created=20->=20from=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WhereYouAd/domains/user/presentation/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0ba1032..78f5163 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 @@ -55,7 +55,7 @@ public ResponseEntity> sendEmailForPwdReset(@Req EmailSentResponse emailSentResponse = emailService.sendEmailForPwd(request.email()); return ResponseEntity.ok( - DataResponse.created(emailSentResponse) + DataResponse.from(emailSentResponse) ); } From 9fba55f84bb608b42da2c76fe7b3fa2bf4d3c2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Sat, 31 Jan 2026 17:05:07 +0900 Subject: [PATCH 15/16] =?UTF-8?q?:bug:=20fix:=20=EC=B5=9C=EC=B4=88=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EB=AC=B8=EA=B5=AC=20/=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=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EB=AC=B8=EA=B5=AC=20=EB=B6=84=EB=A6=AC,?= =?UTF-8?q?=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9D=B8=EC=A6=9D=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=203=EB=B6=84=EC=9C=BC=EB=A1=9C=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 | 20 +++++++++++-------- 1 file changed, 12 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 f7e201a..89b68b4 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,7 +1,7 @@ package com.whereyouad.WhereYouAd.domains.user.domain.service; import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse; -import com.whereyouad.WhereYouAd.domains.user.exception.UserException; +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.repository.UserRepository; import com.whereyouad.WhereYouAd.global.utils.RedisUtil; @@ -34,20 +34,23 @@ public EmailSentResponse sendEmail(String toEmail) { throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 중복 예외(회원가입 시 사용했던 예외) } - return emailSendTemplate(toEmail); + String type = "회원가입"; + + return emailSendTemplate(toEmail, type); } //비밀번호 재설정을 위한 인증코드 이메일 발송 로직 (이미 회원가입 된 상태에서 비밀번호 재설정) public EmailSentResponse sendEmailForPwd(String toEmail) { if (userRepository.existsByEmail(toEmail)) { //이미 회원가입 되어있는 것이 확인되면 - return emailSendTemplate(toEmail); //정상적으로 이메일 발송 + String type = "비밀번호 재설정"; + return emailSendTemplate(toEmail, type); //정상적으로 이메일 발송 } else { //만약 회원가입 되어있지 않다면 throw new UserHandler(UserErrorCode.USER_NOT_FOUND); //예외발생 } } //기존 이메일 발송 로직 템플릿 화 - private EmailSentResponse emailSendTemplate(String toEmail) { + private EmailSentResponse emailSendTemplate(String toEmail, String type) { //인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제 String redisKey = "CODE:" + toEmail; @@ -73,7 +76,8 @@ private EmailSentResponse emailSendTemplate(String toEmail) { SimpleMailMessage message = new SimpleMailMessage(); message.setTo(toEmail); message.setSubject("whereyouad 회원가입 인증번호"); - message.setText("인증번호: " + authCode); + //어떤 유형의 인증(최초 회원가입 or 비밀번호 재설정) 인지 구분하여 인증코드 발송 + message.setText("[Where You Ad] " + type + "\n 인증 번호는 [" + authCode + "] 입니다."); message.setFrom(senderEmail); emailSender.send(message); //만약 실제 존재하는 이메일인데 사용자가 오타를 냈다면 @@ -83,12 +87,12 @@ private EmailSentResponse emailSendTemplate(String toEmail) { } - //Redis에 저장 (Key: "CODE:이메일", Value: "123456", 유효시간: 300초(5분)) + //Redis에 저장 (Key: "CODE:이메일", Value: "123456", 유효시간: 180초(3분)) //테스트 계정도 인증은 해야하니 Redis 에 코드가 저장 되어야 함. //테스트 계정의 인증은 서버 로그를 통해 인증코드를 얻어 입력. - redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 5L); + redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 3L); - return new EmailSentResponse("인증코드를 이메일로 전송했습니다.", toEmail, 300L); + return new EmailSentResponse("인증코드를 이메일로 전송했습니다.", toEmail, 180L); } //인증코드 검증 메서드 From 9e460c8217217440bbb0aa53a57d13ce40e2ca06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Sat, 31 Jan 2026 17:25:08 +0900 Subject: [PATCH 16/16] =?UTF-8?q?:recycle:=20refactor:=20=EC=B5=9C?= =?UTF-8?q?=EC=B4=88=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EB=AC=B8=EA=B5=AC=20/=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=AC=B8=EA=B5=AC=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20->=20Subject=20=EB=AC=B8=EA=B5=AC=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WhereYouAd/domains/user/domain/service/EmailService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 89b68b4..ad7ff56 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 @@ -75,8 +75,8 @@ private EmailSentResponse emailSendTemplate(String toEmail, String type) { //실제 인증 코드가 담긴 이메일 전송 SimpleMailMessage message = new SimpleMailMessage(); message.setTo(toEmail); - message.setSubject("whereyouad 회원가입 인증번호"); //어떤 유형의 인증(최초 회원가입 or 비밀번호 재설정) 인지 구분하여 인증코드 발송 + message.setSubject("whereyouad " + type + " 인증번호"); message.setText("[Where You Ad] " + type + "\n 인증 번호는 [" + authCode + "] 입니다."); message.setFrom(senderEmail);