Skip to content

Commit f1420d7

Browse files
authored
Merge pull request #66 from IT-Cotato/feat/#65-mypage-change-password
Feat/#65 mypage change password
2 parents 5da4c28 + ea96c60 commit f1420d7

File tree

4 files changed

+74
-1
lines changed

4 files changed

+74
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.finsight.finsight.domain.mypage.application.dto.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
public record ChangePasswordRequest(
6+
@NotBlank(message = "현재 비밀번호는 필수입니다.")
7+
String currentPassword,
8+
9+
@NotBlank(message = "새 비밀번호는 필수입니다.")
10+
String newPassword
11+
) {}

src/main/java/com/finsight/finsight/domain/mypage/domain/service/MypageService.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.finsight.finsight.domain.category.domain.service.CategoryService;
77
import com.finsight.finsight.domain.category.persistence.repository.UserCategoryOrderRepository;
88
import com.finsight.finsight.domain.category.persistence.repository.UserCategoryRepository;
9+
import com.finsight.finsight.domain.mypage.application.dto.request.ChangePasswordRequest;
910
import com.finsight.finsight.domain.mypage.application.dto.request.UpdateProfileRequest;
1011
import com.finsight.finsight.domain.mypage.application.dto.response.LearningReportResponse;
1112
import com.finsight.finsight.domain.mypage.application.dto.response.MypageResponse;
@@ -18,9 +19,12 @@
1819
import com.finsight.finsight.domain.storage.persistence.entity.FolderType;
1920
import com.finsight.finsight.domain.storage.persistence.repository.FolderItemRepository;
2021
import com.finsight.finsight.domain.storage.persistence.repository.FolderRepository;
22+
import com.finsight.finsight.domain.user.domain.constant.AuthType;
23+
import com.finsight.finsight.domain.user.persistence.entity.UserAuthEntity;
2124
import com.finsight.finsight.domain.user.persistence.entity.UserEntity;
2225
import com.finsight.finsight.domain.user.persistence.repository.UserRepository;
2326
import lombok.RequiredArgsConstructor;
27+
import org.springframework.security.crypto.password.PasswordEncoder;
2428
import org.springframework.stereotype.Service;
2529
import org.springframework.transaction.annotation.Transactional;
2630

@@ -46,6 +50,7 @@ public class MypageService {
4650
private final QuizAttemptRepository quizAttemptRepository;
4751
private final FolderRepository folderRepository;
4852
private final UserCategoryOrderRepository userCategoryOrderRepository;
53+
private final PasswordEncoder passwordEncoder;
4954

5055
public MypageResponse.MemberProfileResponse getUserProfile(Long userId) {
5156
// db에서 조회에 실패한 경우
@@ -266,6 +271,39 @@ private List<LearningReportResponse.ChecklistStatus> buildChecklistStatus(
266271
return result;
267272
}
268273

274+
/**
275+
* 비밀번호 변경 (카카오 계정 불가)
276+
*/
277+
@Transactional
278+
public void changePassword(Long userId, ChangePasswordRequest request) {
279+
UserEntity user = userRepository.findById(userId)
280+
.orElseThrow(() -> new MypageException(MypageErrorCode.MEMBER_NOT_FOUND));
281+
282+
// 카카오 계정 체크 (EMAIL 타입이 없으면 카카오 전용 계정)
283+
UserAuthEntity auth = user.getUserAuths().stream()
284+
.filter(a -> a.getAuthType() == AuthType.EMAIL)
285+
.findFirst()
286+
.orElseThrow(() -> new MypageException(MypageErrorCode.KAKAO_PASSWORD_NOT_ALLOWED));
287+
288+
// 현재 비밀번호 확인
289+
if (!passwordEncoder.matches(request.currentPassword(), auth.getPasswordHash())) {
290+
throw new MypageException(MypageErrorCode.INVALID_CURRENT_PASSWORD);
291+
}
292+
293+
// 새 비밀번호 형식 검증
294+
if (!request.newPassword().matches("^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z\\d]{6,18}$")) {
295+
throw new MypageException(MypageErrorCode.INVALID_NEW_PASSWORD_FORMAT);
296+
}
297+
298+
// 현재와 동일한지 체크
299+
if (passwordEncoder.matches(request.newPassword(), auth.getPasswordHash())) {
300+
throw new MypageException(MypageErrorCode.SAME_AS_CURRENT_PASSWORD);
301+
}
302+
303+
// 비밀번호 변경
304+
auth.updatePassword(passwordEncoder.encode(request.newPassword()));
305+
}
306+
269307
private String getDayOfWeekKorean(DayOfWeek dayOfWeek) {
270308
return switch (dayOfWeek) {
271309
case MONDAY -> "월";

src/main/java/com/finsight/finsight/domain/mypage/exception/code/MypageErrorCode.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ public enum MypageErrorCode implements BaseErrorCode {
1111

1212
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER-001", "유저 정보를 찾지 못했습니다."),
1313
UNAUTHORIZED_ACCESS(HttpStatus.UNAUTHORIZED, "USER-002", "허용되지 않은 유저입니다."),
14-
DUPLICATE_NICKNAME(HttpStatus.CONFLICT, "USER-003", "이미 사용 중인 닉네임입니다.");
14+
DUPLICATE_NICKNAME(HttpStatus.CONFLICT, "USER-003", "이미 사용 중인 닉네임입니다."),
15+
16+
// 비밀번호 변경
17+
INVALID_CURRENT_PASSWORD(HttpStatus.BAD_REQUEST, "USER-004", "현재 비밀번호가 일치하지 않습니다."),
18+
INVALID_NEW_PASSWORD_FORMAT(HttpStatus.BAD_REQUEST, "USER-005", "비밀번호는 영문, 숫자 조합 6-18자리여야 합니다."),
19+
SAME_AS_CURRENT_PASSWORD(HttpStatus.BAD_REQUEST, "USER-006", "현재 비밀번호와 동일합니다."),
20+
KAKAO_PASSWORD_NOT_ALLOWED(HttpStatus.BAD_REQUEST, "USER-007", "카카오 계정은 비밀번호를 변경할 수 없습니다.");
1521

1622
private final HttpStatus httpStatus;
1723
private final String message;

src/main/java/com/finsight/finsight/domain/mypage/presentation/MypageController.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.finsight.finsight.domain.mypage.presentation;
22

33
import com.finsight.finsight.domain.auth.application.dto.request.CheckNicknameRequest;
4+
import com.finsight.finsight.domain.mypage.application.dto.request.ChangePasswordRequest;
45
import com.finsight.finsight.domain.mypage.application.dto.request.UpdateProfileRequest;
56
import com.finsight.finsight.domain.mypage.application.dto.response.LearningReportResponse;
67
import com.finsight.finsight.domain.mypage.application.dto.response.MypageResponse;
@@ -123,4 +124,21 @@ public ResponseEntity<DataResponse<LearningReportResponse.Report>> getLearningRe
123124
return ResponseEntity.ok(
124125
DataResponse.from(myPageService.getLearningReport(userId, targetMonday, targetSunday, validWeeksAgo)));
125126
}
127+
128+
@Operation(summary = "비밀번호 변경", description = "현재 비밀번호를 확인 후 새 비밀번호로 변경합니다. (카카오 계정 불가)")
129+
@ApiResponses({
130+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "비밀번호 변경 성공"),
131+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "비밀번호 변경 실패")
132+
})
133+
@PutMapping("/me/change-password")
134+
public ResponseEntity<DataResponse<Void>> changePassword(
135+
@AuthenticationPrincipal CustomUserDetails customUserDetails,
136+
@Valid @RequestBody ChangePasswordRequest request
137+
) {
138+
if (customUserDetails == null) {
139+
throw new MypageException(MypageErrorCode.UNAUTHORIZED_ACCESS);
140+
}
141+
myPageService.changePassword(customUserDetails.getUserId(), request);
142+
return ResponseEntity.ok(DataResponse.ok());
143+
}
126144
}

0 commit comments

Comments
 (0)