[BE] SIC1-202 [FEAT] 아이디 및 비밀번호 찾기 api 구현#89
Hidden character warning
Conversation
Walkthrough비밀번호 재설정 기능이 추가되었습니다: 이메일 코드 발송·검증, Redis에 코드·재설정 토큰 저장·검증, 토큰으로 비밀번호 변경, 관련 컨트롤러 엔드포인트·인터페이스·구현·템플릿·단위테스트가 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Controller as UserController
participant UserSvc as UserServiceImpl
participant EmailSvc as EmailService
participant Redis
participant DB as UserRepository
participant Encoder as PasswordEncoder
participant Refresh as RefreshTokenService
rect rgb(220,240,255)
Note over User,Controller: 이메일/ID 찾기
User->>Controller: POST /api/user/id/find (name, phone)
Controller->>UserSvc: findEmailByNameAndPhone(name, phone)
UserSvc->>DB: findByNameAndPhoneNumber(...)
DB-->>UserSvc: User or empty
UserSvc-->>Controller: 이메일 또는 404
end
rect rgb(240,255,240)
Note over User,Controller: 재설정 코드 발송 & 검증
User->>Controller: POST /api/user/password/reset/send (email)
Controller->>UserSvc: passwordReset(email)
UserSvc->>EmailSvc: sendResetEmail(email)
EmailSvc->>Redis: SET PASSWORD_RESET:{email} = code (TTL)
EmailSvc-->>User: 이메일 발송
User->>Controller: POST /api/user/password/reset/verify (email, code)
Controller->>UserSvc: verifyResetCodeAndIssueToken(email, code)
UserSvc->>EmailSvc: verifyResetEmail(email, code)
EmailSvc->>Redis: GET PASSWORD_RESET:{email} -> compare -> DEL on success
EmailSvc-->>UserSvc: verified
UserSvc->>Redis: SET PASSWORD_RESET:{UUID} = email (TTL for token)
UserSvc-->>Controller: return resetToken
end
rect rgb(255,245,230)
Note over User,Controller: 토큰으로 비밀번호 변경
User->>Controller: POST /api/user/password/reset/commit (resetToken, newPassword)
Controller->>UserSvc: resetPasswordByToken(resetToken, newPassword)
UserSvc->>Redis: GET PASSWORD_RESET:{token} -> email
Redis-->>UserSvc: email
UserSvc->>DB: findByEmail(email)
DB-->>UserSvc: User
UserSvc->>Encoder: encode(newPassword)
Encoder-->>UserSvc: encodedPwd
UserSvc->>DB: update password, save
UserSvc->>Refresh: deleteRefreshTokens(userId)
UserSvc->>Redis: DEL PASSWORD_RESET:{token}
UserSvc-->>Controller: success
Controller-->>User: 200 OK
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 검토 집중 포인트:
Possibly related PRs
Suggested reviewers
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
backend/src/main/resources/templates/mail/resetEmail.html (1)
1-23: 코드 유효시간 문구를 설정값과 동기화해 주세요본문에
3분이 하드코딩되어 있는데, 실제 만료 시간은EmailProperties.getCodeExpire()값에 의해 결정됩니다. 운영 중 만료 시간을 조정하면 사용자에게 노출되는 문구와 실제 동작이 어긋나 혼선을 줄 수 있으니, 서비스에서 만료 시간을 템플릿 변수로 내려주거나 i18n 메시지에서 설정 기반으로 렌더링하도록 조정해 보시길 권장드립니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dao/UserRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserService.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(4 hunks)backend/src/main/resources/templates/mail/resetEmail.html(1 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(5 hunks)
🔇 Additional comments (3)
backend/src/main/java/org/sejongisc/backend/user/dao/UserRepository.java (1)
19-19: 이름+전화번호 조회 시그니처 적절합니다이름과 전화번호로 단건을 찾아오는 흐름이 신규 서비스 요구와 잘 맞고, 기존에
existsByPhoneNumber로 전화번호 중복을 막고 있으니Optional<User>반환 형태도 자연스럽습니다.backend/src/main/java/org/sejongisc/backend/user/service/UserService.java (1)
23-29: 비밀번호 복구용 인터페이스 확장 👍컨트롤러와 구현체에서 사용하는 4가지 복구 메서드가 인터페이스에 정리되어 계약이 명확해졌습니다. 유지보수 시 호출 지점을 추적하기도 쉬워질 것 같습니다.
backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)
423-453: 토큰 기반 재설정 플로우 테스트 좋아요비밀번호 해시 갱신, 리프레시 토큰 제거, Redis 키 삭제까지 모두 검증해 비밀번호 재설정 시나리오의 회귀를 확실하게 막아줄 것 같습니다.
backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java(1 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
[high] 257-257: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
195-200: 이름/전화번호 입력에서 선행·후행 공백을 제거해 주세요현재 그대로 조회하면 사용자가 이름이나 전화번호에 실수로 공백을 포함했을 때 매칭이 실패해 이메일을 찾지 못합니다. 서비스 레이어에서 한 번만
trim()해서 빈 문자열까지 걸러 주면 UX가 훨씬 견고해집니다.public String findEmailByNameAndPhone(String name, String phone){ - return userRepository.findByNameAndPhoneNumber(name, phone) + if (name == null || phone == null) { + return null; + } + String normalizedName = name.trim(); + String normalizedPhone = phone.trim(); + if (normalizedName.isEmpty() || normalizedPhone.isEmpty()) { + return null; + } + + return userRepository.findByNameAndPhoneNumber(normalizedName, normalizedPhone) .map(User::getEmail) .orElse(null); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
264-266: Redis 토큰 삭제 실패를 기록하도록 조정하세요Line 264-266에서 예외를 완전히 무시하면 삭제 실패가 숨어버려 토큰이 TTL 동안 남는 상황을 감지하기 어렵습니다. 최소한 경고 로그를 남기고, 필요 시 후속 조치를 고려해 주세요.
- try { - redisTemplate.delete("PASSWORD_RESET:" + resetToken); - } catch (Exception ignored) {} + try { + redisTemplate.delete("PASSWORD_RESET:" + resetToken); + } catch (Exception e) { + log.warn("비밀번호 재설정 토큰 삭제 실패: token={}", resetToken, e); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
203-209: 비회원 이메일 요청을 조용히 처리한 점 확인Line 203-209에서 존재하지 않는 이메일에 대해 조용히 반환하도록 수정해 사용자 열거 위험을 줄인 것이 적절합니다.
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
271-315: LGTM - 비밀번호 재설정 로직이 매우 안전하게 구현되었습니다Redis 조회/삭제 실패 시 즉시 예외를 발생시키고, 비밀번호 trim → 검증 → 인코딩 순서가 올바르며, 토큰 삭제 및 리프레시 토큰 무효화까지 완벽하게 처리되었습니다. 이전 리뷰의 모든 critical/major 이슈가 해결되었습니다.
선택적 정리: 주석 처리된 코드 제거
Line 274의 주석 처리된 코드는 제거하는 것이 코드 가독성에 좋습니다.
-// String email = (String) redisTemplate.opsForValue().get("PASSWORD_RESET:" + resetToken); String email = null;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/util/PasswordPolicyValidator.java (1)
PasswordPolicyValidator(9-22)
🔇 Additional comments (5)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (5)
4-9: LGTM - 필요한 의존성이 올바르게 추가되었습니다비밀번호 재설정 기능 구현에 필요한 EmailService, RefreshTokenService, RedisTemplate, PasswordPolicyValidator 등의 의존성이 적절하게 추가되었습니다.
Also applies to: 28-30, 32-32, 44-46
61-74: LGTM - 비밀번호 처리 로직이 올바르게 개선되었습니다이전 리뷰에서 지적된 공백 패딩 우회 문제가 완벽하게 해결되었습니다. trim → null/공백 검증 → 정책 검증 → 인코딩 순서로 처리하여
"Aa1! "와 같은 입력으로 정책을 우회할 수 없습니다.
203-216: LGTM - 입력값 정규화가 올바르게 적용되었습니다이름과 전화번호를 trim하여 정규화한 후 검증 및 조회를 수행하므로, 사용자가 실수로 공백을 포함하더라도 정상적으로 이메일을 찾을 수 있습니다. 이전 리뷰의 지적 사항이 완벽하게 반영되었습니다.
218-236: LGTM - 이메일 정규화 및 보안 처리가 우수합니다이메일을 trim하여 정규화한 후 검증 및 조회를 수행하므로 공백이 포함된 입력에도 정상 동작합니다. 존재하지 않는 이메일에 대해 조용히 반환하는 것은 이메일 열거 공격을 방지하는 좋은 보안 관행입니다.
238-269: LGTM - 입력 검증과 에러 처리가 매우 견고합니다이메일과 코드를 정규화하여 검증하고, Redis 저장 실패 시 즉시 예외를 발생시켜 일관성 없는 상태를 방지합니다. 이전 리뷰에서 지적된 모든 사항(입력 정규화, Redis 오류 처리)이 완벽하게 해결되었습니다.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)
19-19: 사용하지 않는 import 제거를 권장합니다.
MediaType를 import했지만 코드에서 실제로 사용하지 않고 있습니다. 모든@Content어노테이션에서 문자열 리터럴"application/json"을 사용하고 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)
🧰 Additional context used
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
[high] 257-257: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetVerifyRequest.java (1)
12-14: 인증코드 입력을 숫자 형식으로 제한해야 합니다.현재는 길이만 6자리로 제한되어 공백이나 특수문자도 그대로 통과합니다. Redis에 저장된 값은 숫자 6자리이므로, DTO 단계에서 숫자만 허용하도록 방어하면 검증 로직이 더 견고해집니다.
@@ -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; @@ - @NotBlank(message = "인증코드는 필수입니다.") - @Size(min = 6, max = 6, message = "인증코드는 6자리여야 합니다.") - String code + @NotBlank(message = "인증코드는 필수입니다.") + @Size(min = 6, max = 6, message = "인증코드는 6자리여야 합니다.") + @Pattern(regexp = "^\\d{6}$", message = "인증코드는 숫자 6자리여야 합니다.") + String code
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetCommitRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetSendRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetVerifyRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/UserIdFindRequest.java(1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
[high] 256-256: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (3)
backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetCommitRequest.java (1)
10-15: 비밀번호 정책 검증이 DTO 차원에서 잘 정의되어 있습니다.정규식으로 정책을 명확히 강제해 서비스 로직이 단순해집니다.
backend/src/main/java/org/sejongisc/backend/user/dto/UserIdFindRequest.java (1)
9-11: 전화번호 검증 패턴이 요구 사항을 정확히 반영합니다.010 국번과 8자리 숫자만 허용해 잘못된 요청을 초기에 걸러낼 수 있습니다.
backend/src/main/java/org/sejongisc/backend/user/dto/PasswordResetSendRequest.java (1)
7-9: 이메일 입력 검증이 간결하고 확실합니다.필수 여부와 형식 검증이 갖춰져 있어 컨트롤러 진입 전에 오류를 차단할 수 있습니다.
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)
78-78: 개인정보 로깅을 제거하거나 마스킹해야 합니다.신규 엔드포인트(Line 236)에서는 개인정보 로깅을 제거했으나, 기존 코드인 이 줄에서는 여전히 이메일을 로그에 남기고 있습니다. 일관성을 위해 개인정보 보호 정책을 적용해야 합니다.
다음과 같이 수정하세요:
-log.info("email: {} 권한: {}", user.getUsername(), user.getAuthorities()); +log.info("사용자 정보 조회 - 권한: {}", user.getAuthorities());
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)
186-197: 검증된 DTO 사용으로 타입 안정성이 개선되었습니다.과거 리뷰 코멘트를 반영하여
Map<String, String>대신@Valid UserIdFindRequest를 사용하도록 수정되었습니다. 입력 검증이 적절히 적용되었습니다.참고: Line 191의 null 체크 대신 서비스 레이어에서 예외를 던지도록 하면 에러 처리가 더 일관적일 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)
🧰 Additional context used
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
[high] 256-256: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (4)
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (4)
15-15: DTO 패키지 import가 적절하게 추가되었습니다.새로운 DTO들을 사용하기 위한 import가 올바르게 추가되었습니다.
Also applies to: 18-18
233-239: 과거 리뷰 코멘트가 모두 반영되었습니다.
@Valid PasswordResetSendRequestDTO 사용으로 타입 안정성 확보 ✓trim()을 통한 공백 정규화 적용 ✓- 개인정보 로깅 제거 ✓
모든 개선 사항이 적절히 구현되었습니다.
275-282: 공백 정규화가 적절히 적용되었습니다.과거 리뷰에서 지적된 공백 처리 문제가 해결되었습니다.
code모두에trim()을 적용하여 Redis 키 매칭 실패를 방지합니다.
325-329: 검증된 DTO 사용으로 구현이 개선되었습니다.
@Valid PasswordResetCommitRequestDTO를 사용하여 비밀번호 정책 검증이 DTO 레벨에서 수행됩니다. 구현이 간결하고 명확합니다.참고: 정적 분석 도구가 Line 256에서 API 키를 감지했다고 보고했으나, 이는 Swagger 예제의 UUID 문자열이므로 false positive입니다.
Summary by CodeRabbit
새로운 기능
문서
테스트