From d6d1fcac18f87fbb40fdd264352b6f6af7509c5a Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Thu, 24 Jul 2025 14:00:53 +0900 Subject: [PATCH 1/6] =?UTF-8?q?:recycle:=20refactor:=20MemberQueryService?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/{ => query}/MemberQueryService.java | 2 +- .../member/service/{ => query}/MemberQueryServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/org/withtime/be/withtimebe/domain/member/service/{ => query}/MemberQueryService.java (67%) rename src/main/java/org/withtime/be/withtimebe/domain/member/service/{ => query}/MemberQueryServiceImpl.java (91%) diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryService.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryService.java similarity index 67% rename from src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryService.java rename to src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryService.java index d5b99a8..8c9a21f 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryService.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryService.java @@ -1,4 +1,4 @@ -package org.withtime.be.withtimebe.domain.member.service; +package org.withtime.be.withtimebe.domain.member.service.query; import org.withtime.be.withtimebe.domain.member.entity.Member; diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryServiceImpl.java similarity index 91% rename from src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryServiceImpl.java rename to src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryServiceImpl.java index b853a3d..b14f0fe 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/service/MemberQueryServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/query/MemberQueryServiceImpl.java @@ -1,4 +1,4 @@ -package org.withtime.be.withtimebe.domain.member.service; +package org.withtime.be.withtimebe.domain.member.service.query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; From d7eca01aff0998133673881b86ca471cb28ed7ad Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Thu, 24 Jul 2025 22:00:42 +0900 Subject: [PATCH 2/6] =?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=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=98=A4?= =?UTF-8?q?=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 --- .../domain/auth/service/command/AuthCommandServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java index aaa3057..907e121 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java @@ -39,7 +39,7 @@ public class AuthCommandServiceImpl implements AuthCommandService { public void signUp(AuthRequestDTO.SignUp request) { validateSignUp(request); - Member member = memberRepository.save(AuthConverter.toLocalMember(request.email(), request.username(), request.socialId() != null ? passwordEncoder.encode(request.password()) : null, request.phoneNumber(), request.gender(), request.birth())); + Member member = memberRepository.save(AuthConverter.toLocalMember(request.email(), request.username(), request.socialId() != null ? null : passwordEncoder.encode(request.password()), request.phoneNumber(), request.gender(), request.birth())); if (request.socialId() != null) { Social social = socialRepository.findById(request.socialId()).orElseThrow(() -> new SocialException(SocialErrorCode.NOT_FOUND_SOCIAL)); From f7e1dcd7d1ddf19e0a8be7994ec1ab0fed0e16c9 Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Thu, 24 Jul 2025 22:10:57 +0900 Subject: [PATCH 3/6] =?UTF-8?q?:bug:=20fix:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=ED=95=84=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../withtime/be/withtimebe/domain/member/entity/Member.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java b/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java index c97e6a7..7ff6f40 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java @@ -42,9 +42,6 @@ public class Member extends BaseEntity { @Column(name = "password") private String password; - @Column(name = "nickname") - private String nickname; - @Column(name = "gender") @Enumerated(EnumType.STRING) private Gender gender; From 53884d48ddff8b5d1e5c04a867e5ddd1354793f0 Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Thu, 24 Jul 2025 22:23:34 +0900 Subject: [PATCH 4/6] =?UTF-8?q?:sparkles:=20feat:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 78 +++++++++++++++++++ .../member/converter/MemberConverter.java | 13 ++++ .../domain/member/dto/MemberRequestDTO.java | 17 ++++ .../domain/member/dto/MemberResponseDTO.java | 12 +++ .../domain/member/entity/Member.java | 8 ++ .../service/command/MemberCommandService.java | 10 +++ .../command/MemberCommandServiceImpl.java | 58 ++++++++++++++ .../global/error/code/AuthErrorCode.java | 3 +- .../global/error/code/MemberErrorCode.java | 1 + .../global/security/SecurityConfig.java | 2 +- .../global/security/filter/JwtFilter.java | 2 +- 11 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/controller/MemberController.java create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/converter/MemberConverter.java create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberRequestDTO.java create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberResponseDTO.java create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java create mode 100644 src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/controller/MemberController.java b/src/main/java/org/withtime/be/withtimebe/domain/member/controller/MemberController.java new file mode 100644 index 0000000..4220e46 --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/controller/MemberController.java @@ -0,0 +1,78 @@ +package org.withtime.be.withtimebe.domain.member.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.namul.api.payload.response.DefaultResponse; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.withtime.be.withtimebe.domain.member.converter.MemberConverter; +import org.withtime.be.withtimebe.domain.member.dto.MemberRequestDTO; +import org.withtime.be.withtimebe.domain.member.dto.MemberResponseDTO; +import org.withtime.be.withtimebe.domain.member.entity.Member; +import org.withtime.be.withtimebe.domain.member.service.command.MemberCommandService; +import org.withtime.be.withtimebe.global.security.annotation.AuthenticatedMember; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/members") +@Tag(name = "사용자 API") +public class MemberController { + + private final MemberCommandService memberCommandService; + + @Operation(summary = "비밀번호 변경 API", description = "현재 비밀번호가 맞으면 새로운 비밀번호로 변경") + @ApiResponses({ + @ApiResponse(responseCode = "204", description = "비밀번호 변경 성공"), + @ApiResponse( + responseCode = "400", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - AUTH400_2: 소셜 로그인으로 가입된 사용자입니다. + - MEMBER400_1: 이전 비밀번호와 동일합니다. + """ + ), + @ApiResponse( + responseCode = "401", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - AUTH401_2: 현재 비밀번호가 맞지 않습니다. + """ + ), + @ApiResponse( + responseCode = "404", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - MEMBER404_1: 사용자를 찾지 못했습니다. + """ + ), + }) + @PatchMapping("/passwords") + public DefaultResponse changePassword(@AuthenticatedMember Member member, + @RequestBody MemberRequestDTO.ChangePassword request) { + memberCommandService.changePassword(member, request); + return DefaultResponse.noContent(); + } + + @Operation(summary = "사용자 정보 변경 API", description = "사용자 정보 변경, 사용자를 쿠키로 인식하여 정보를 변경") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "정보 변경 성공"), + @ApiResponse( + responseCode = "404", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - MEMBER404_1: 사용자를 찾지 못했습니다. + """ + ) + }) + @PatchMapping("/infos") + public DefaultResponse changeInfo(@AuthenticatedMember Member member, + @RequestBody MemberRequestDTO.ChangeInfo request) { + Member updatedMember = memberCommandService.changeInfo(member.getId(), request); + return DefaultResponse.ok(MemberConverter.toChangeInfo(updatedMember)); + } +} diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/converter/MemberConverter.java b/src/main/java/org/withtime/be/withtimebe/domain/member/converter/MemberConverter.java new file mode 100644 index 0000000..02e018f --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/converter/MemberConverter.java @@ -0,0 +1,13 @@ +package org.withtime.be.withtimebe.domain.member.converter; + +import org.withtime.be.withtimebe.domain.member.dto.MemberResponseDTO; +import org.withtime.be.withtimebe.domain.member.entity.Member; + +public class MemberConverter { + + public static MemberResponseDTO.ChangeInfo toChangeInfo(Member member) { + return MemberResponseDTO.ChangeInfo.builder() + .username(member.getUsername()) + .build(); + } +} diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberRequestDTO.java b/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberRequestDTO.java new file mode 100644 index 0000000..05471d7 --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberRequestDTO.java @@ -0,0 +1,17 @@ +package org.withtime.be.withtimebe.domain.member.dto; + +public record MemberRequestDTO() { + + public record ChangePassword( + String nowPassword, + String newPassword + ) { + + } + + public record ChangeInfo( + String username + ) { + + } +} diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberResponseDTO.java b/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberResponseDTO.java new file mode 100644 index 0000000..c3225ea --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/dto/MemberResponseDTO.java @@ -0,0 +1,12 @@ +package org.withtime.be.withtimebe.domain.member.dto; + +import lombok.Builder; + +public record MemberResponseDTO() { + @Builder + public record ChangeInfo( + String username + ) { + + } +} diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java b/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java index 7ff6f40..d838788 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/entity/Member.java @@ -55,4 +55,12 @@ public class Member extends BaseEntity { @Enumerated(EnumType.STRING) @Column(name = "role", nullable = false) private Role role; + + public void changeUsername(String newUsername) { + this.username= newUsername; + } + + public void changePassword(String encodedPassword) { + this.password = encodedPassword; + } } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java new file mode 100644 index 0000000..a6d33e1 --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java @@ -0,0 +1,10 @@ +package org.withtime.be.withtimebe.domain.member.service.command; + +import org.withtime.be.withtimebe.domain.member.dto.MemberRequestDTO; +import org.withtime.be.withtimebe.domain.member.entity.Member; + +public interface MemberCommandService { + void changePassword(Member member, MemberRequestDTO.ChangePassword request); + void changePassword(Long memberId, String password); + Member changeInfo(Long memberId, MemberRequestDTO.ChangeInfo request); +} diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java new file mode 100644 index 0000000..2d7f7ed --- /dev/null +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java @@ -0,0 +1,58 @@ +package org.withtime.be.withtimebe.domain.member.service.command; + +import lombok.RequiredArgsConstructor; +import org.namul.api.payload.error.exception.ServerApplicationException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.withtime.be.withtimebe.domain.member.dto.MemberRequestDTO; +import org.withtime.be.withtimebe.domain.member.entity.Member; +import org.withtime.be.withtimebe.domain.member.repository.MemberRepository; +import org.withtime.be.withtimebe.global.error.code.AuthErrorCode; +import org.withtime.be.withtimebe.global.error.code.MemberErrorCode; +import org.withtime.be.withtimebe.global.error.exception.AuthException; +import org.withtime.be.withtimebe.global.error.exception.MemberException; + +@Service +@RequiredArgsConstructor +@Transactional +public class MemberCommandServiceImpl implements MemberCommandService { + + private final PasswordEncoder passwordEncoder; + private final MemberRepository memberRepository; + + + @Override + public void changePassword(Member member, MemberRequestDTO.ChangePassword request) { + if (member.getPassword() != null && !passwordEncoder.matches(request.nowPassword(), member.getPassword())) { + throw new AuthException(AuthErrorCode.INCORRECT_PASSWORD); + } + this.changePassword(member.getId(), request.newPassword()); + } + + @Override + public void changePassword(Long memberId, String password) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> + new MemberException(MemberErrorCode.NOT_FOUND)); + validateChangePassword(member, password); + member.changePassword(passwordEncoder.encode(password)); + } + + @Override + public Member changeInfo(Long memberId, MemberRequestDTO.ChangeInfo request) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> + new MemberException(MemberErrorCode.NOT_FOUND)); + member.changeUsername(request.username()); + return member; + } + + public void validateChangePassword(Member member, String password) throws ServerApplicationException { + String memberPassword = member.getPassword(); + if (memberPassword == null) { + throw new AuthException(AuthErrorCode.ONLY_AVAILABLE_SOCIAL); + } + else if (passwordEncoder.matches(password, memberPassword)) { + throw new MemberException(MemberErrorCode.SAME_PASSWORD); + } + } +} diff --git a/src/main/java/org/withtime/be/withtimebe/global/error/code/AuthErrorCode.java b/src/main/java/org/withtime/be/withtimebe/global/error/code/AuthErrorCode.java index dc0d7e9..b67e846 100644 --- a/src/main/java/org/withtime/be/withtimebe/global/error/code/AuthErrorCode.java +++ b/src/main/java/org/withtime/be/withtimebe/global/error/code/AuthErrorCode.java @@ -11,7 +11,8 @@ public enum AuthErrorCode implements BaseErrorCode { NOT_FOUND_LOGIN_MEMBER(HttpStatus.NOT_FOUND, "AUTH404_1", "해당 이메일을 찾을 수 없습니다."), FAIL_AUTH_LOGIN(HttpStatus.UNAUTHORIZED, "AUTH401_1", "일반 로그인에 실패했습니다."), ALREADY_EXIST_EMAIL(HttpStatus.BAD_REQUEST, "AUTH400_1", "이미 존재하는 이메일입니다."), - ONLY_AVAILABLE_SOCIAL(HttpStatus.BAD_REQUEST, "AUTH400_2", "소셜 로그인만 가능합니다.") + ONLY_AVAILABLE_SOCIAL(HttpStatus.BAD_REQUEST, "AUTH400_2", "소셜 로그인만 가능합니다."), + INCORRECT_PASSWORD(HttpStatus.UNAUTHORIZED, "AUTH401_2", "비밀번호가 맞지 않습니다.") ; private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/org/withtime/be/withtimebe/global/error/code/MemberErrorCode.java b/src/main/java/org/withtime/be/withtimebe/global/error/code/MemberErrorCode.java index 587e3be..7504456 100644 --- a/src/main/java/org/withtime/be/withtimebe/global/error/code/MemberErrorCode.java +++ b/src/main/java/org/withtime/be/withtimebe/global/error/code/MemberErrorCode.java @@ -9,6 +9,7 @@ public enum MemberErrorCode implements BaseErrorCode { NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404_1", "사용자를 찾지 못했습니다."), + SAME_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER400_1", "이전 비밀번호와 동일합니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/org/withtime/be/withtimebe/global/security/SecurityConfig.java b/src/main/java/org/withtime/be/withtimebe/global/security/SecurityConfig.java index fec718b..7d0ff94 100644 --- a/src/main/java/org/withtime/be/withtimebe/global/security/SecurityConfig.java +++ b/src/main/java/org/withtime/be/withtimebe/global/security/SecurityConfig.java @@ -28,7 +28,7 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.withtime.be.withtimebe.domain.auth.service.query.TokenStorageQueryService; -import org.withtime.be.withtimebe.domain.member.service.MemberQueryService; +import org.withtime.be.withtimebe.domain.member.service.query.MemberQueryService; import org.withtime.be.withtimebe.global.security.filter.JsonLoginFilter; import org.withtime.be.withtimebe.global.security.filter.JwtFilter; import org.withtime.be.withtimebe.global.security.handler.CustomAccessDeniedHandler; diff --git a/src/main/java/org/withtime/be/withtimebe/global/security/filter/JwtFilter.java b/src/main/java/org/withtime/be/withtimebe/global/security/filter/JwtFilter.java index fd23f49..5792111 100644 --- a/src/main/java/org/withtime/be/withtimebe/global/security/filter/JwtFilter.java +++ b/src/main/java/org/withtime/be/withtimebe/global/security/filter/JwtFilter.java @@ -20,7 +20,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.withtime.be.withtimebe.domain.auth.service.query.TokenStorageQueryService; import org.withtime.be.withtimebe.domain.member.entity.Member; -import org.withtime.be.withtimebe.domain.member.service.MemberQueryService; +import org.withtime.be.withtimebe.domain.member.service.query.MemberQueryService; import org.withtime.be.withtimebe.global.security.constants.AuthenticationConstants; import org.withtime.be.withtimebe.global.security.domain.CustomUserDetails; import org.withtime.be.withtimebe.global.util.CookieUtil; From e3ca2dde4e638b12a9416efdd87c0d101ff9f482 Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Fri, 25 Jul 2025 16:24:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?:bug:=20fix:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=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 --- .../domain/member/service/command/MemberCommandService.java | 2 +- .../member/service/command/MemberCommandServiceImpl.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java index a6d33e1..eed5c12 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandService.java @@ -5,6 +5,6 @@ public interface MemberCommandService { void changePassword(Member member, MemberRequestDTO.ChangePassword request); - void changePassword(Long memberId, String password); + void changePassword(String email, String password); Member changeInfo(Long memberId, MemberRequestDTO.ChangeInfo request); } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java index 2d7f7ed..48f16ec 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/service/command/MemberCommandServiceImpl.java @@ -27,12 +27,12 @@ public void changePassword(Member member, MemberRequestDTO.ChangePassword reques if (member.getPassword() != null && !passwordEncoder.matches(request.nowPassword(), member.getPassword())) { throw new AuthException(AuthErrorCode.INCORRECT_PASSWORD); } - this.changePassword(member.getId(), request.newPassword()); + this.changePassword(member.getEmail(), request.newPassword()); } @Override - public void changePassword(Long memberId, String password) { - Member member = memberRepository.findById(memberId).orElseThrow(() -> + public void changePassword(String email, String password) { + Member member = memberRepository.findByEmail(email).orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND)); validateChangePassword(member, password); member.changePassword(passwordEncoder.encode(password)); From 4b7382ddfa1fc3df798d9574a8141d8173a4e6ff Mon Sep 17 00:00:00 2001 From: Jeongmo Seo Date: Fri, 25 Jul 2025 16:34:24 +0900 Subject: [PATCH 6/6] =?UTF-8?q?:sparkles:=20feat:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=B0=BE=EA=B8=B0=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 32 +++++++++++++++++++ .../auth/dto/request/AuthRequestDTO.java | 7 ++++ .../service/command/AuthCommandService.java | 2 ++ .../command/AuthCommandServiceImpl.java | 10 ++++++ 4 files changed, 51 insertions(+) diff --git a/src/main/java/org/withtime/be/withtimebe/domain/auth/controller/AuthController.java b/src/main/java/org/withtime/be/withtimebe/domain/auth/controller/AuthController.java index f34509e..b8d5307 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/auth/controller/AuthController.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/auth/controller/AuthController.java @@ -124,4 +124,36 @@ public DefaultResponse checkVerificationCode(@Valid @RequestBody EmailRe emailCommandService.checkEmail(request); return DefaultResponse.noContent(); } + + @Operation(summary = "비밀번호 찾기 API", description = "이메일 인증 이후 이메일과 새로운 비밀번호로 비밀번호 변경") + @ApiResponses({ + @ApiResponse(responseCode = "204", description = "비밀번호 변경 성공"), + @ApiResponse( + responseCode = "400", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - AUTH400_2: 소셜 로그인으로 가입된 사용자입니다. + - MEMBER400_1: 이전 비밀번호와 동일합니다. + """ + ), + @ApiResponse( + responseCode = "401", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - EMAIL401_2: 비밀번호 재설정에 이메일 인증을 하지 않았습니다. + """ + ), + @ApiResponse( + responseCode = "404", + description = """ + 다음과 같은 이유로 실패할 수 있습니다: + - MEMBER404_1: 사용자를 찾지 못했습니다. + """ + ), + }) + @PostMapping("/passwords") + public DefaultResponse findPassword(@RequestBody AuthRequestDTO.FindPassword request) { + authCommandService.findPassword(request); + return DefaultResponse.noContent(); + } } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/auth/dto/request/AuthRequestDTO.java b/src/main/java/org/withtime/be/withtimebe/domain/auth/dto/request/AuthRequestDTO.java index 858b1c3..b1a21d7 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/auth/dto/request/AuthRequestDTO.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/auth/dto/request/AuthRequestDTO.java @@ -27,4 +27,11 @@ public record SignUp( ) { } + + public record FindPassword( + String email, + String newPassword + ) { + + } } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandService.java b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandService.java index 165f06c..5150223 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandService.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandService.java @@ -2,10 +2,12 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.RequestBody; import org.withtime.be.withtimebe.domain.auth.dto.request.AuthRequestDTO; public interface AuthCommandService { void signUp(AuthRequestDTO.SignUp request); void reissueToken(HttpServletRequest request, HttpServletResponse response); void logout(HttpServletRequest request, HttpServletResponse response); + void findPassword(@RequestBody AuthRequestDTO.FindPassword request); } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java index 907e121..d06c43e 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/auth/service/command/AuthCommandServiceImpl.java @@ -15,6 +15,7 @@ import org.withtime.be.withtimebe.domain.member.entity.Social; import org.withtime.be.withtimebe.domain.member.repository.MemberRepository; import org.withtime.be.withtimebe.domain.member.repository.SocialRepository; +import org.withtime.be.withtimebe.domain.member.service.command.MemberCommandService; import org.withtime.be.withtimebe.global.error.code.*; import org.withtime.be.withtimebe.global.error.exception.*; import org.withtime.be.withtimebe.global.security.constants.AuthenticationConstants; @@ -29,6 +30,7 @@ public class AuthCommandServiceImpl implements AuthCommandService { private final PasswordEncoder passwordEncoder; private final MemberRepository memberRepository; private final SocialRepository socialRepository; + private final MemberCommandService memberCommandService; private final TokenCommandService tokenCommandService; private final TokenStorageCommandService tokenStorageCommandService; private final TokenQueryService tokenQueryService; @@ -82,6 +84,14 @@ public void logout(HttpServletRequest request, HttpServletResponse response) { CookieUtil.deleteCookie(request, response, AuthenticationConstants.REFRESH_TOKEN_NAME); } + @Override + public void findPassword(AuthRequestDTO.FindPassword request) { + if (!emailVerificationCodeStorageQueryService.isVerified(request.email())) { + throw new EmailException(EmailErrorCode.UNVERIFIED_EMAIL); + } + memberCommandService.changePassword(request.email(), request.newPassword()); + } + private void reissueTokenInCookie(HttpServletRequest request, HttpServletResponse response, Long userId) { Member member = memberRepository.findById(userId).orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND)