-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 메일 인증 구현 #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이 PR은 인증/메일 기능을 추가·개편합니다. 이메일 인증 코드 발송/검증, 비밀번호 찾기/변경 엔드포인트 및 DTO/템플릿을 신설하고, 토큰 재발급/로그아웃 흐름을 RefreshTokenRequest 기반으로 변경합니다. ErrorCode/BusinessException을 확장하고, 메일 및 .env 설정을 활성화합니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant AC as AuthController
participant MS as MailService
participant UR as UserRepository
participant RS as RedisService
participant JMS as JavaMailSender
participant TPL as Thymeleaf
rect rgb(235,245,255)
note over C,AC: 이메일 인증코드 발송
C->>AC: POST /api/auth/email/send {email}
AC->>MS: sendMail(email)
MS->>UR: 이메일 중복 검사
MS->>MS: 6자리 코드 생성
MS->>TPL: 템플릿 렌더링(코드)
MS->>JMS: 메일 전송
MS->>RS: code 저장(5분 TTL)
AC-->>C: 200 OK
end
rect rgb(245,255,235)
note over C,AC: 인증코드 검증
C->>AC: POST /api/auth/email/validation {email, authCode}
AC->>MS: checkAuthCode(email, code)
MS->>RS: 저장 코드 조회/검증(VERIFIED 상태 반영)
AC-->>C: 200 OK
end
sequenceDiagram
autonumber
participant C as Client
participant AC as AuthController
participant JS as JwtService
rect rgb(255,245,235)
note over C,AC: 토큰 재발급
C->>AC: POST /api/auth/reissue {refreshToken}
AC->>JS: reissueTokens(request)
JS->>JS: 리프레시 토큰 검증/재발급
JS-->>AC: ReissueResponse{access, refresh}
AC-->>C: 200 OK (JSON 본문)
end
rect rgb(255,240,245)
note over C,AC: 로그아웃
C->>AC: POST /api/auth/logout {refreshToken}
AC->>JS: logout(request, tokenReq)
JS->>JS: 토큰 블랙리스트/무효화 처리
AC-->>C: 200 OK
end
sequenceDiagram
autonumber
participant C as Client
participant AC as AuthController
participant US as UserService
participant MS as MailService
participant UR as UserRepository
participant ENC as PasswordEncoder
participant JMS as JavaMailSender
participant TPL as Thymeleaf
rect rgb(240,255,255)
note over C,AC: 비밀번호 찾기(임시 비번 메일)
C->>AC: POST /api/auth/email/find-password {email}
AC->>US: sendNewPassword(email)
US->>MS: sendPasswordMail(email)
MS->>MS: 8자 임시 비번 생성
MS->>TPL: 템플릿 렌더링(비번)
MS->>JMS: 메일 전송
US->>UR: 사용자 비밀번호 업데이트(인코딩)
AC-->>C: 200 OK
end
rect rgb(245,245,255)
note over C,AC: 내 비밀번호 변경
C->>AC: PATCH /api/auth/password {prePassword,newPassword}
AC->>US: updateMyPassword(userId, req)
US->>UR: 사용자 조회/검증
US->>ENC: 새 비밀번호 인코딩
US->>UR: 저장
AC-->>C: 200 OK
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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. 🧪 Early access (Sonnet 4.5): enabledWe are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience. Note:
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Nitpick comments (18)
src/main/resources/application.yml (1)
66-74: 메일 설정 활성화 완료, Gmail 앱 비밀번호 확인 권장Gmail SMTP 설정이 잘 구성되어 있습니다. STARTTLS와 인증이 활성화되어 보안도 적절하네요.
참고사항:
- Gmail 계정에서 2단계 인증을 사용 중이라면 일반 비밀번호가 아닌 앱 비밀번호를
MAIL_PASSWORD에 설정해야 합니다.- SMTP 타임아웃이 5초로 설정되어 있는데, 네트워크 환경에 따라 짧을 수 있으니 프로덕션 환경에서 모니터링해보세요.
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/response/ReissueResponse.java (1)
1-11: 깔끔한 DTO 구조토큰 재발급 응답을 위한 DTO가 잘 정의되어 있습니다. Lombok의
@Builder와@Getter로 간결하게 작성되었네요.선택적 개선 제안:
필드를final로 선언하면 불변성을 보장할 수 있어 더 안전합니다:@Getter @Builder public class ReissueResponse { - private String accessToken; - private String refreshToken; + private final String accessToken; + private final String refreshToken; }src/main/resources/templates/Password-email.html (1)
1-20: 템플릿 구조 정상, 선택적 개선 제안임시 비밀번호를 담은 이메일 템플릿이 잘 작성되어 있습니다. Thymeleaf 표현식도 올바르게 사용되었네요.
선택적 개선 사항:
- HTML 구조 완성:
<head>,<meta charset="UTF-8">,<title>태그를 추가하면 이메일 클라이언트 호환성이 높아집니다.- 인라인 스타일 유지 관리: 인라인 스타일은 이메일 템플릿에서는 일반적이지만, 여러 템플릿에서 공통 스타일을 재사용한다면 외부 CSS나 템플릿 프래그먼트로 분리하는 것도 고려해볼 만합니다.
- 접근성: "임시 비밀번호입니다" 문구가 파란색으로만 구분되는데, 텍스트 자체로 충분히 의미가 전달되니 큰 문제는 없습니다.
src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/exception/BusinessException.java (1)
10-13: ErrorCode 메시지 통합 확인
ErrorCode의getMessage()메서드를 활용하도록 생성자가 업데이트되었습니다. 잘 동작할 것 같네요.사소한 네이밍 일관성 제안:
파라미터명이code인데 필드명은errorCode로 남아있어 약간의 불일치가 있습니다. 개인적으로는 파라미터도errorCode로 유지하는 게 더 명확하다고 생각하지만, 현재 코드로도 충분히 이해 가능합니다.-public BusinessException(ErrorCode code) { - super(code.getMessage()); - this.errorCode = code; +public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode;src/main/resources/templates/AuthCode-email.html (1)
1-20: 인증 코드 템플릿 정상 작동회원가입 인증 번호를 담은 이메일 템플릿이 올바르게 구성되어 있습니다.
${code}표현식도 잘 사용되었네요.선택적 개선 사항 (Password-email.html과 동일):
- HTML 구조 완성:
<head>,<meta charset="UTF-8">,<title>태그 추가 권장- 스타일 재사용: 두 템플릿(AuthCode-email.html, Password-email.html)에서 거의 동일한 인라인 스타일을 사용 중이므로, Thymeleaf 프래그먼트나 공통 레이아웃으로 분리하면 유지보수가 편해집니다.
예시 (Thymeleaf 프래그먼트):
<!-- fragments/email-layout.html --> <div th:fragment="email-box(title, content)" align="center" style="border:1px solid black; font-family:verdana;"> <h3 style="color:blue" th:text="${title}"> </h3> <div style="font-size:130%" th:text="${content}"> </div> </div>사용:
<div th:replace="~{fragments/email-layout :: email-box('회원가입 인증 번호 입니다.', ${code})}"></div>src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/MailRequest.java (1)
3-3: 사용하지 않는 import가 있네요.
@Schema어노테이션을 import 했는데 실제로는 사용하지 않고 있어요. 만약 API 문서화를 위해 추가하려던 거라면 필드에 적용하고, 아니라면 import를 정리하는 게 좋겠습니다.src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/JwtService.java (2)
47-47: objectMapper 필드가 사용되지 않네요.
objectMapper를 주입받고 있지만 이 파일 내에서는 실제로 사용하지 않고 있어요. 필요 없다면 제거하는 게 좋겠습니다.- private final ObjectMapper objectMapper;
12-12: 사용하지 않는 import들이 있어요.
ObjectMapper,MediaType,IOException을 import 했는데 실제로는 사용하지 않고 있네요. 코드 정리를 위해 제거하는 게 좋겠습니다.-import com.WhoIsRoom.WhoIs_Server.global.common.response.BaseResponse; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.MediaType; -import java.io.IOException;Also applies to: 18-18, 21-21
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java (3)
34-36: 이메일 인증 확인 로직이 추가됐네요.회원가입 시 이메일 인증 여부를 체크하는 건 좋은 보안 조치예요. 다만
"VERIFIED"같은 매직 스트링보다는 상수나 enum으로 관리하면 더 안전하고 유지보수하기 좋을 것 같아요.예를 들어:
public enum VerificationStatus { VERIFIED, PENDING, EXPIRED }그리고 이렇게 사용:
- if (!"VERIFIED".equals(mailService.getStoredCode(request.getEmail()))){ + if (!VerificationStatus.VERIFIED.name().equals(mailService.getStoredCode(request.getEmail()))){
55-64: 비밀번호 변경 로직에서 몇 가지 개선점이 있어요.현재 로직은 기본적으로 잘 작동하지만, 몇 가지 보완하면 더 좋을 것 같아요:
새 비밀번호 검증:
newPassword가 이전 비밀번호와 같은지, 또는 비밀번호 정책(길이, 복잡도)을 만족하는지 검증이 없네요.User 엔티티 캡슐화:
user.setPassword()를 직접 호출하고 있는데, User 엔티티에changePassword(String oldPassword, String newPassword)같은 메서드를 두면 비즈니스 로직이 엔티티에 집중되고 더 안전해요.User 엔티티에 다음과 같은 메서드를 추가하는 걸 고려해보세요:
// User.java public void changePassword(String oldPassword, String newPassword, PasswordEncoder encoder) { if (!encoder.matches(oldPassword, this.password)) { throw new BusinessException(ErrorCode.INVALID_PASSWORD); } if (oldPassword.equals(newPassword)) { throw new BusinessException(ErrorCode.SAME_PASSWORD); } // 비밀번호 정책 검증 validatePasswordPolicy(newPassword); this.password = encoder.encode(newPassword); }그러면 서비스 코드는 이렇게 단순해져요:
user.changePassword(request.getPrePassword(), request.getNewPassword(), passwordEncoder);
47-53: 트랜잭션 경계 및 메일 발송 분리 검토, 임시 비밀번호 생성 강화, 만료·변경 강제 정책 추가
- 현재
sendNewPassword는 메일 발송 성공 후에만user.setPassword가 실행되어 전송 실패 시 DB 변경이 발생하지 않습니다. 다만 I/O 로직이 트랜잭션 안에서 실행되면 트랜잭션 유지 시간이 길어지고 장애 시 롤백 복잡성이 커지므로, 메일 발송을 트랜잭션 외부 또는 별도 이벤트/비동기로 분리하는 방안을 검토해주세요.createNewPassword()가java.util.Random으로 8자 영숫자만 생성하는데, 보안 강화를 위해java.security.SecureRandom사용, 특수문자 포함, 최소 12자 이상 등 복잡도 기준을 충족하도록 개선이 필요합니다.- 임시 비밀번호 사용 후 첫 로그인 시 변경을 강제하는 플래그나 만료 일시(expiration timestamp) 필드를 추가해 보안성을 높여주세요.
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/CodeCheckRequest.java (2)
12-13: 필드 검증 어노테이션 추가를 추천합니다.
@NotNull임포트는 되어 있는데 필드에는 적용되지 않았네요.authCode가 null로 들어오면MailService.checkAuthCode에서 NPE가 발생할 수 있습니다.다음과 같이 수정하는 것을 고려해보세요:
+ @NotNull private String email; + @NotNull private String authCode;문자열이라
@NotBlank를 사용하는 것도 좋은 선택입니다 (빈 문자열도 방어).
11-13: 필드에 유효성 검증 어노테이션을 추가하는 것이 좋겠습니다.
@NotNull을 임포트하고 있지만 실제 필드에는 적용되지 않았네요.authCode필드에@NotNull과다음과 같이 수정해보세요:
+@Schema(description = "이메일 인증 코드 검증 요청") public class CodeCheckRequest { + @NotNull(message = "이메일은 필수입니다") + @Email(message = "올바른 이메일 형식이 아닙니다") + @Schema(description = "사용자 이메일", example = "[email protected]") private String email; + @NotNull(message = "인증 코드는 필수입니다") + @Schema(description = "6자리 인증 코드", example = "123456") private String authCode; }src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java (4)
41-45: 이메일 인증 코드 발송 엔드포인트 추가되었습니다.새로운
/email/send엔드포인트가 추가되었네요.요청 바디 검증을 위해
@Valid어노테이션을 추가하는 것을 권장합니다:- public BaseResponse<Void> sendAuthCodeMail(@RequestBody MailRequest request) { + public BaseResponse<Void> sendAuthCodeMail(@Valid @RequestBody MailRequest request) {
MailRequest에 이메일 형식 검증(
47-51: 인증 코드 검증 엔드포인트 추가되었습니다.
CodeCheckRequest를 받아서 검증하는 엔드포인트네요.앞서
CodeCheckRequest에서 제안한 것처럼, 여기에도@Valid를 추가하면 좋습니다:- public BaseResponse<Void> checkAuthCode(@RequestBody CodeCheckRequest request) { + public BaseResponse<Void> checkAuthCode(@Valid @RequestBody CodeCheckRequest request) {Null 체크를 컨트롤러 레벨에서 자동으로 처리할 수 있습니다.
53-57: 비밀번호 찾기 기능 추가되었습니다.임시 비밀번호를 이메일로 발송하는 엔드포인트네요.
UserService.sendNewPassword에서 임시 비밀번호를 생성하고 DB에 저장하는 것 확인했습니다.다른 엔드포인트와 마찬가지로
@Valid를 추가하세요:- public BaseResponse<Void> findPassword(@RequestBody MailRequest request) { + public BaseResponse<Void> findPassword(@Valid @RequestBody MailRequest request) {
59-64: 비밀번호 변경 엔드포인트 추가되었습니다.인증된 사용자가 자신의 비밀번호를 변경하는 엔드포인트네요.
@CurrentUserId로 사용자를 식별하고,UserService에서 기존 비밀번호를 검증하는 것 확인했습니다. 보안적으로 올바른 구조입니다.일관성을 위해
@Valid를 추가하세요:public BaseResponse<Void> updatePassword(@CurrentUserId Long userId, - @RequestBody PasswordRequest request) { + @Valid @RequestBody PasswordRequest request) {
PasswordRequest에 필드 검증 어노테이션이 있다면 자동으로 검증됩니다.src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java (1)
134-149: 인증 코드 검증 로직 확인 & 스타일 개선전체 흐름은 올바릅니다:
- 만료 체크 ✓
- 이미 인증된 경우 idempotent 처리 ✓
- 코드 불일치 에러 ✓
- 성공 시 "VERIFIED" 마킹 + 30분 TTL ✓
다만 Line 141의 불필요한 세미콜론은 제거하는 게 좋습니다.
// 인증 번호가 이미 인증된 상태인 경우 그냥 리턴 - if ("VERIFIED".equals(getStoredCode(request.getEmail()))){return;}; + if ("VERIFIED".equals(getStoredCode(request.getEmail()))) { + return; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
.gitignore(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java(2 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/CodeCheckRequest.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/MailRequest.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/PasswordRequest.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/RefreshTokenRequest.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/response/ReissueResponse.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/handler/success/CustomAuthenticationSuccessHandler.java(0 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/JwtService.java(4 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java(4 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/exception/BusinessException.java(1 hunks)src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java(1 hunks)src/main/resources/application.yml(2 hunks)src/main/resources/templates/AuthCode-email.html(1 hunks)src/main/resources/templates/Password-email.html(1 hunks)
💤 Files with no reviewable changes (1)
- src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/handler/success/CustomAuthenticationSuccessHandler.java
🧰 Additional context used
🧬 Code graph analysis (6)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/RefreshTokenRequest.java (1)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/CodeCheckRequest.java (1)
NoArgsConstructor(9-14)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java (3)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java (1)
Slf4j(18-65)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/JwtService.java (1)
Slf4j(24-113)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java (1)
Slf4j(26-169)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/MailRequest.java (1)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/CodeCheckRequest.java (1)
NoArgsConstructor(9-14)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/JwtService.java (4)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java (1)
Slf4j(18-65)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/handler/success/CustomAuthenticationSuccessHandler.java (1)
Slf4j(22-63)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/util/JwtUtil.java (1)
Slf4j(18-134)src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/redis/RedisService.java (1)
Service(11-39)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/PasswordRequest.java (1)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/CodeCheckRequest.java (1)
NoArgsConstructor(9-14)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java (3)
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java (1)
Slf4j(18-65)src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java (1)
Slf4j(18-65)src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/redis/RedisService.java (1)
Service(11-39)
🔇 Additional comments (14)
.gitignore (1)
38-41: 환경 변수 파일 보호 - 좋습니다!
.env파일을 gitignore에 추가한 것은 이메일 인증 기능에 필요한 민감한 정보(SMTP 자격 증명, API 키 등)를 보호하는 좋은 보안 관행입니다.src/main/resources/application.yml (1)
7-7: 포맷팅 일관성 개선 확인공백 추가로 가독성이 약간 나아졌네요.
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/JwtService.java (2)
49-62: 로그아웃 로직이 잘 구현되어 있어요!refreshToken의 타입을 검증하고, 블랙리스트 처리까지 깔끔하게 되어 있네요. RefreshTokenRequest DTO를 통해 타입 안정성도 확보했고요. 잘 작성된 코드입니다.
64-71: 토큰 재발급 흐름이 깔끔해졌네요.이전에는 response에 직접 헤더를 쓰는 방식이었던 것 같은데, ReissueResponse 객체를 반환하는 방식으로 변경해서 더 명확하고 테스트하기 좋은 구조가 됐어요.
src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java (2)
44-45: 새로운 에러 코드 추가 잘 되었습니다!
INVALID_PASSWORD에러 코드가 깔끔하게 추가되었고,UserService.updateMyPassword에서 기존 비밀번호 검증 실패 시 사용되는 것 확인했습니다. 메시지도 명확하고 HTTP 상태 코드도 적절하네요.
44-49: 새로운 에러 코드와 메시지 필드 추가가 적절합니다.비밀번호 변경 시나리오를 위한
INVALID_PASSWORD추가와 각 에러 코드에message필드를 도입한 것이 좋습니다. 로그인 실패(INVALID_ID_OR_PASSWORD)와 기존 비밀번호 불일치(INVALID_PASSWORD)를 명확히 구분할 수 있게 되었네요.src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java (4)
25-26: 의존성 주입 깔끔합니다!
MailService와UserService가 생성자 주입 방식으로 추가되었고, 새로운 이메일/비밀번호 기능을 위해 필요한 구성이네요.
29-31: 로그아웃 API 변경사항 확인했습니다.Refresh 토큰을 헤더가 아닌 요청 바디(
RefreshTokenRequest)로 받는 방식으로 변경되었네요. 기존 클라이언트와의 호환성이 깨지는 변경이니, 프론트엔드 팀이나 API 문서 업데이트가 필요합니다.클라이언트 코드 및 API 문서가 이 변경사항을 반영했는지 확인해주세요.
36-39: 토큰 재발급 응답 구조 개선되었습니다.재발급된 토큰을 응답 바디에 JSON으로 반환하도록 변경되었네요. 헤더 기반보다 명확하고 프론트에서 처리하기 편한 방식입니다. 다만 기존 클라이언트는 호환되지 않으니 마이그레이션 필요합니다.
클라이언트가 응답 바디에서
accessToken과refreshToken을 읽도록 업데이트되었는지 확인해주세요.
28-39: 토큰 재발급 응답을 JSON 바디로 변경한 것이 좋습니다.
reissueTokens엔드포인트가 이제ReissueResponse를 바디에 담아 반환하고,logout과reissue모두RefreshTokenRequest를 요청 바디로 받도록 일관성 있게 구성되었네요. 헤더 대신 JSON 응답으로 토큰을 전달하는 것이 클라이언트 처리에 더 명확합니다.src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java (4)
46-62: 메일 전송 로직 확인 완료이메일 중복 체크 → 인증 코드 생성 → 메일 전송 → Redis 저장 순서가 적절합니다. 메일 전송 실패 시 Redis에 저장하지 않는 것도 올바른 흐름입니다.
다만 회원가입 시점까지 이메일 중복 체크(Line 18 in UserService)를 다시 수행하고 있으므로, 여기서의 중복 체크와 실제 가입 사이의 race condition은 이미 방어되고 있네요.
64-73: 임시 비밀번호 전송 로직 확인임시 비밀번호 생성 → 메일 전송 → 비밀번호 반환 흐름이 깔끔합니다.
UserService.sendNewPassword에서 사용자 존재 여부를 먼저 확인하므로 여기서는 별도 검증이 불필요하네요.
104-132: 이메일 메시지 생성 로직 확인HTML 이메일 구성이 적절하고, UTF-8 인코딩 설정도 올바릅니다.
MessagingException발생 시BusinessException으로 변환하는 것도 일관된 예외 처리 전략이네요.템플릿 파일(
AuthCode-email.html,Password-email.html)이src/main/resources/templates/경로에 정상적으로 존재하는지 확인해 주세요.
156-168: 템플릿 렌더링 로직 확인Thymeleaf 컨텍스트 생성과 변수 설정이 깔끔합니다. 템플릿 처리 중 발생할 수 있는 예외는 Spring의 글로벌 예외 핸들러에서 처리하는 구조로 보이므로 별도 try-catch 없이 두는 것도 합리적입니다.
| @PostMapping("/email/send") | ||
| public BaseResponse<Void> sendAuthCodeMail(@RequestBody MailRequest request) { | ||
| mailService.sendMail(request); | ||
| return BaseResponse.ok(null); | ||
| } | ||
|
|
||
| @PostMapping("/email/validation") | ||
| public BaseResponse<Void> checkAuthCode(@RequestBody CodeCheckRequest request) { | ||
| mailService.checkAuthCode(request); | ||
| return BaseResponse.ok(null); | ||
| } | ||
|
|
||
| @PostMapping("/email/find-password") | ||
| public BaseResponse<Void> findPassword(@RequestBody MailRequest request) { | ||
| userService.sendNewPassword(request); | ||
| return BaseResponse.ok(null); | ||
| } | ||
|
|
||
| @PatchMapping("/password") | ||
| public BaseResponse<Void> updatePassword(@CurrentUserId Long userId, | ||
| @RequestBody PasswordRequest request) { | ||
| userService.updateMyPassword(userId, request); | ||
| return BaseResponse.ok(null); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
요청 바디에 @Valid 어노테이션을 추가하세요.
새로 추가된 모든 엔드포인트(sendAuthCodeMail, checkAuthCode, findPassword, updatePassword)의 요청 바디 파라미터에 @Valid를 붙이지 않으면 DTO에 선언된 유효성 검증 어노테이션(@NotNull, @Email 등)이 실행되지 않습니다.
다음과 같이 수정하세요:
@PostMapping("/email/send")
-public BaseResponse<Void> sendAuthCodeMail(@RequestBody MailRequest request) {
+public BaseResponse<Void> sendAuthCodeMail(@Valid @RequestBody MailRequest request) {
mailService.sendMail(request);
return BaseResponse.ok(null);
}
@PostMapping("/email/validation")
-public BaseResponse<Void> checkAuthCode(@RequestBody CodeCheckRequest request) {
+public BaseResponse<Void> checkAuthCode(@Valid @RequestBody CodeCheckRequest request) {
mailService.checkAuthCode(request);
return BaseResponse.ok(null);
}
@PostMapping("/email/find-password")
-public BaseResponse<Void> findPassword(@RequestBody MailRequest request) {
+public BaseResponse<Void> findPassword(@Valid @RequestBody MailRequest request) {
userService.sendNewPassword(request);
return BaseResponse.ok(null);
}
@PatchMapping("/password")
public BaseResponse<Void> updatePassword(@CurrentUserId Long userId,
- @RequestBody PasswordRequest request) {
+ @Valid @RequestBody PasswordRequest request) {
userService.updateMyPassword(userId, request);
return BaseResponse.ok(null);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @PostMapping("/email/send") | |
| public BaseResponse<Void> sendAuthCodeMail(@RequestBody MailRequest request) { | |
| mailService.sendMail(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PostMapping("/email/validation") | |
| public BaseResponse<Void> checkAuthCode(@RequestBody CodeCheckRequest request) { | |
| mailService.checkAuthCode(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PostMapping("/email/find-password") | |
| public BaseResponse<Void> findPassword(@RequestBody MailRequest request) { | |
| userService.sendNewPassword(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PatchMapping("/password") | |
| public BaseResponse<Void> updatePassword(@CurrentUserId Long userId, | |
| @RequestBody PasswordRequest request) { | |
| userService.updateMyPassword(userId, request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PostMapping("/email/send") | |
| public BaseResponse<Void> sendAuthCodeMail(@Valid @RequestBody MailRequest request) { | |
| mailService.sendMail(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PostMapping("/email/validation") | |
| public BaseResponse<Void> checkAuthCode(@Valid @RequestBody CodeCheckRequest request) { | |
| mailService.checkAuthCode(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PostMapping("/email/find-password") | |
| public BaseResponse<Void> findPassword(@Valid @RequestBody MailRequest request) { | |
| userService.sendNewPassword(request); | |
| return BaseResponse.ok(null); | |
| } | |
| @PatchMapping("/password") | |
| public BaseResponse<Void> updatePassword(@CurrentUserId Long userId, | |
| @Valid @RequestBody PasswordRequest request) { | |
| userService.updateMyPassword(userId, request); | |
| return BaseResponse.ok(null); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java
around lines 41 to 64, the request body parameters for sendAuthCodeMail,
checkAuthCode, findPassword and updatePassword are missing @Valid so DTO
validation annotations won't run; add the @Valid annotation to each @RequestBody
parameter (e.g. @Valid @RequestBody MailRequest request, etc.) and ensure the
appropriate Valid import (javax.validation.Valid or jakarta.validation.Valid
consistent with the project) is added to the imports.
| @PostMapping("/email/find-password") | ||
| public BaseResponse<Void> findPassword(@RequestBody MailRequest request) { | ||
| userService.sendNewPassword(request); | ||
| return BaseResponse.ok(null); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
비밀번호 찾기 엔드포인트에 추가 보안 검증이 필요합니다.
현재 구현은 이메일만으로 비밀번호를 재설정하므로, 이메일 주소만 알면 누구나 다른 사용자의 비밀번호를 변경할 수 있습니다. 다음 보안 강화를 고려해보세요:
- Rate limiting: 동일 이메일에 대한 비밀번호 재설정 요청 빈도를 제한 (예: 5분에 1회)
- 추가 인증 단계: 임시 비밀번호를 즉시 변경하지 않고, 이메일로 전송된 토큰/링크를 통한 검증 후 사용자가 직접 새 비밀번호를 설정하도록 변경
- 알림: 비밀번호 재설정 시 기존 세션 무효화 및 사용자에게 알림 전송
현재 프로젝트에 rate limiting 설정이 있는지 확인하려면 다음 스크립트를 실행하세요:
🏁 Script executed:
#!/bin/bash
# Rate limiting 관련 설정 확인
echo "=== Checking for rate limiting configurations ==="
rg -i "ratelimit|rate.limit|bucket4j|resilience4j" --type java --type yaml
echo -e "\n=== Checking for interceptor/filter configurations ==="
rg -i "interceptor|filter.*registration" --type java -A 3Length of output: 105
비밀번호 재설정 엔드포인트에 필수 보안 조치 적용
- 현재 이메일만으로
AuthController.findPassword(src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java:53)에서 즉시 새 비밀번호가 발급되어, 악의적 요청 시 계정 탈취 위험이 있습니다. - 다음 보안 강화 필수 적용:
- Rate limiting (예: 동일 이메일 5분당 1회 제한)
- 이메일 토큰/링크 검증 후 사용자가 직접 비밀번호 재설정
- 기존 세션 무효화 및 사용자 알림
🤖 Prompt for AI Agents
In
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/controller/AuthController.java
around lines 53-57, the findPassword endpoint currently issues a new password
immediately when given an email; change this to a secure reset flow: replace
immediate password issuance with generation of a single-use, time-limited token
stored server-side (or in a signed JWT) and send a reset link containing that
token to the user's email; enforce rate limiting per email (e.g., allow one
token request per 5 minutes) before generating a token; implement a separate
POST endpoint to accept the token and new password, verify token validity and
expiry, set the new password there, invalidate all existing sessions/tokens for
that user upon successful reset, and send an email notification of the password
change; update UserService to support token creation/verification, rate-limiting
checks, session invalidation, and notification.
| public class MailRequest { | ||
| private String email; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
이메일 필드에 validation이 빠져있어요.
@NotNull을 import 했지만 실제로 필드에 적용하지 않았네요. 이메일은 인증 코드 발송과 비밀번호 찾기에 사용되는 중요한 필드인 만큼, @NotBlank와 @Email 검증을 추가하는 게 좋겠어요.
다음처럼 수정해보세요:
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MailRequest {
+ @NotBlank(message = "이메일은 필수입니다")
+ @Email(message = "유효한 이메일 형식이 아닙니다")
private String email;
}🤖 Prompt for AI Agents
In
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/MailRequest.java
around lines 11-12, the email field lacks validation annotations: add @NotBlank
and @Email to the private String email field (replace or supplement the unused
@NotNull import), and add the corresponding imports (e.g.,
javax.validation.constraints.NotBlank and javax.validation.constraints.Email or
the jakarta equivalents used in the project) so the field is validated for
presence and correct email format.
| public class PasswordRequest { | ||
| private String prePassword; | ||
| private String newPassword; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
비밀번호 필드에 validation 어노테이션이 빠져있어요.
prePassword와 newPassword 필드에 @NotBlank 같은 검증 어노테이션이 없네요. 비밀번호는 보안에 민감한 데이터인 만큼, 빈 값이나 null이 들어오지 않도록 validation을 추가하는 게 좋겠어요. 필요하다면 @Pattern으로 비밀번호 강도 정책도 적용할 수 있습니다.
다음 diff를 적용해서 기본 검증을 추가해보세요:
package com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request;
+import jakarta.validation.constraints.NotBlank;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PasswordRequest {
+ @NotBlank(message = "이전 비밀번호는 필수입니다")
private String prePassword;
+ @NotBlank(message = "새 비밀번호는 필수입니다")
private String newPassword;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public class PasswordRequest { | |
| private String prePassword; | |
| private String newPassword; | |
| } | |
| package com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request; | |
| import jakarta.validation.constraints.NotBlank; | |
| import lombok.AccessLevel; | |
| import lombok.Getter; | |
| import lombok.NoArgsConstructor; | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class PasswordRequest { | |
| @NotBlank(message = "이전 비밀번호는 필수입니다") | |
| private String prePassword; | |
| @NotBlank(message = "새 비밀번호는 필수입니다") | |
| private String newPassword; | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/PasswordRequest.java
around lines 9 to 12, the password fields lack validation annotations; add
javax.validation constraints to both fields (e.g., @NotBlank on prePassword and
newPassword) and optionally a @Pattern on newPassword to enforce strength, and
import the necessary validation annotations (and update any
Lombok/getters/setters if needed) so incoming requests are validated for
non-null/non-empty values.
| public class RefreshTokenRequest { | ||
| private String refreshToken; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
refreshToken 필드에 validation을 추가해주세요.
토큰 재발급과 로그아웃에 사용되는 중요한 필드인데 validation이 없네요. @NotBlank를 추가해서 빈 값이나 null이 들어오는 걸 방지하는 게 좋겠어요.
package com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request;
+import jakarta.validation.constraints.NotBlank;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RefreshTokenRequest {
+ @NotBlank(message = "리프레시 토큰은 필수입니다")
private String refreshToken;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public class RefreshTokenRequest { | |
| private String refreshToken; | |
| } | |
| package com.WhoIsRoom.WhoIs_Server.domain.auth.dto.request; | |
| import jakarta.validation.constraints.NotBlank; | |
| import lombok.AccessLevel; | |
| import lombok.Getter; | |
| import lombok.NoArgsConstructor; | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class RefreshTokenRequest { | |
| @NotBlank(message = "리프레시 토큰은 필수입니다") | |
| private String refreshToken; | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/dto/request/RefreshTokenRequest.java
around lines 9 to 11, the refreshToken field lacks validation; add the
javax.validation.constraints.NotBlank annotation to the refreshToken field to
prevent null or empty values, and add the corresponding import statement; ensure
the DTO is used in controller methods validated by @Valid (or class-level
validation enabled) so the constraint is enforced at request binding.
| import java.util.Random; | ||
|
|
||
| @Slf4j | ||
| @Transactional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
클래스 레벨 @transactional 사용 주의
클래스 레벨에 @Transactional을 선언하면 모든 public 메서드가 트랜잭션 안에서 실행됩니다. 하지만 이 서비스는 이메일 전송(외부 I/O)을 수행하는데, 메일 전송 중 실패나 지연이 발생하면 DB 커넥션과 트랜잭션을 불필요하게 오래 점유하게 됩니다.
sendMail, sendPasswordMail 같은 메서드는 트랜잭션이 필요 없으므로 클래스 레벨 애노테이션을 제거하고, 실제로 DB 변경이 필요한 메서드에만 개별적으로 @Transactional을 적용하는 것을 권장합니다.
-@Transactional
@Service
@RequiredArgsConstructor
public class MailService {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Transactional | |
| @Service | |
| @RequiredArgsConstructor | |
| public class MailService { | |
| // … existing code … | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java
around line 27, remove the class-level @Transactional so that all public methods
are not implicitly wrapped in transactions; then annotate only the specific
methods that actually modify the database with @Transactional (e.g., apply
@Transactional to repository-write methods), leaving mail-sending methods like
sendMail and sendPasswordMail non-transactional to avoid holding DB connections
during external I/O.
| public String createCode() { | ||
| Random random = new Random(); | ||
| StringBuilder key = new StringBuilder(); | ||
|
|
||
| for (int i = 0; i < 6; i++) { | ||
| key.append(random.nextInt(10)); // 0~9 숫자 | ||
| } | ||
|
|
||
| return key.toString(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보안 취약점: 예측 가능한 인증 코드 생성
java.util.Random은 암호학적으로 안전하지 않아 공격자가 시드를 추측하면 인증 코드를 예측할 수 있습니다. 이메일 인증 코드는 보안이 중요한 영역이므로 반드시 SecureRandom을 사용해야 합니다.
+import java.security.SecureRandom;
+
@Slf4j
@Transactional
@Service
@RequiredArgsConstructor
public class MailService {
+ private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
private static final long VERIFICATION_CODE_EXPIRY_MINUTES = 5; // 인증 번호 6자리를 구현하는 메서드
public String createCode() {
- Random random = new Random();
StringBuilder key = new StringBuilder();
for (int i = 0; i < 6; i++) {
- key.append(random.nextInt(10)); // 0~9 숫자
+ key.append(SECURE_RANDOM.nextInt(10)); // 0~9 숫자
}
return key.toString();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public String createCode() { | |
| Random random = new Random(); | |
| StringBuilder key = new StringBuilder(); | |
| for (int i = 0; i < 6; i++) { | |
| key.append(random.nextInt(10)); // 0~9 숫자 | |
| } | |
| return key.toString(); | |
| } | |
| import java.security.SecureRandom; | |
| @Slf4j | |
| @Transactional | |
| @Service | |
| @RequiredArgsConstructor | |
| public class MailService { | |
| private static final SecureRandom SECURE_RANDOM = new SecureRandom(); | |
| private static final long VERIFICATION_CODE_EXPIRY_MINUTES = 5; | |
| // 인증 번호 6자리를 구현하는 메서드 | |
| public String createCode() { | |
| StringBuilder key = new StringBuilder(); | |
| for (int i = 0; i < 6; i++) { | |
| key.append(SECURE_RANDOM.nextInt(10)); // 0~9 숫자 | |
| } | |
| return key.toString(); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java
around lines 76 to 85, the method uses java.util.Random which is not
cryptographically secure; replace it with java.security.SecureRandom (preferably
a static final instance) and use secureRandom.nextInt(10) for each digit to
generate the 6-digit code, ensuring the generator is reused and not re-seeded
per call to avoid predictability.
| public String createNewPassword() { | ||
| Random random = new Random(); | ||
| StringBuffer key = new StringBuffer(); | ||
|
|
||
| for (int i = 0; i < 8; i++) { | ||
| int index = random.nextInt(4); | ||
|
|
||
| switch (index) { | ||
| case 0: key.append((char) ((int) random.nextInt(26) + 97)); break; | ||
| case 1: key.append((char) ((int) random.nextInt(26) + 65)); break; | ||
| default: key.append(random.nextInt(9)); | ||
| } | ||
| } | ||
| return key.toString(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보안 취약점 & 코드 개선 제안
-
보안 이슈:
createCode()와 동일하게Random대신SecureRandom을 사용해야 합니다. 임시 비밀번호 역시 예측 가능하면 계정 탈취로 이어질 수 있습니다. -
선택적 개선:
StringBuffer→StringBuilder변경 (단일 스레드 환경에서 더 빠름)
// 임시 비밀번호를 구현하는 메서드
public String createNewPassword() {
- Random random = new Random();
- StringBuffer key = new StringBuffer();
+ StringBuilder key = new StringBuilder();
for (int i = 0; i < 8; i++) {
- int index = random.nextInt(4);
+ int index = SECURE_RANDOM.nextInt(4);
switch (index) {
- case 0: key.append((char) ((int) random.nextInt(26) + 97)); break;
- case 1: key.append((char) ((int) random.nextInt(26) + 65)); break;
- default: key.append(random.nextInt(9));
+ case 0: key.append((char) (SECURE_RANDOM.nextInt(26) + 97)); break;
+ case 1: key.append((char) (SECURE_RANDOM.nextInt(26) + 65)); break;
+ default: key.append(SECURE_RANDOM.nextInt(10));
}
}
return key.toString();
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java
around lines 88 to 102, the method uses java.util.Random and StringBuffer;
replace Random with java.security.SecureRandom and StringBuffer with
StringBuilder, instantiate SecureRandom once, and use its nextInt(...) calls in
place of Random to generate characters; additionally adjust digit generation to
nextInt(10) to include 0–9 range. Ensure imports are updated and no
synchronized/wrapper usage is required since StringBuilder and SecureRandom are
thread-safe for per-call use.
| public String getStoredCode(String email) { | ||
| String key = EMAIL_KEY_PREFIX + email; | ||
| return redisService.getValues(key); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Redis 반환값 확인 필요
RedisService.getValues()는 키가 없을 때 "false" 문자열을 반환하는데(RedisService.java Line 15 참조), checkAuthCode 메서드 Line 136에서는 null 체크를 하고 있습니다.
현재 로직대로라면 만료된 코드에 대해 EXPIRED_EMAIL_CODE 예외가 발생하지 않고, Line 144의 비교에서 "false".equals(storedCode)가 실행되어 INVALID_EMAIL_CODE 예외가 발생할 가능성이 있습니다.
다음 중 하나로 수정하세요:
방법 1: null 체크를 "false" 체크로 변경
public void checkAuthCode(CodeCheckRequest request) {
String storedCode = getStoredCode(request.getEmail());
- if (storedCode == null) {
+ if ("false".equals(storedCode)) {
throw new BusinessException(ErrorCode.EXPIRED_EMAIL_CODE);
}방법 2: RedisService가 null을 반환하도록 수정 (RedisService 변경 필요)
🏁 Script executed:
#!/bin/bash
# RedisService.getValues 구현 확인
rg -nP "public String getValues" -C5 .Length of output: 1323
checkAuthCode 만료 로직 수정 필요
getStoredCode()가 키 미존재 시 "false"를 반환하므로 null 체크(if (storedCode == null))는 절대 참이 되지 않습니다. 이로 인해 만료된 코드에서 EXPIRED_EMAIL_CODE 예외가 발생하지 않고, 이후 "false".equals(storedCode) 비교에서 INVALID_EMAIL_CODE 예외가 던져질 수 있습니다.
해결 방법(택1):
- null 체크를
"false"체크로 변경
public void checkAuthCode(CodeCheckRequest request) {
String storedCode = getStoredCode(request.getEmail());
- if (storedCode == null) {
+ if ("false".equals(storedCode)) {
throw new BusinessException(ErrorCode.EXPIRED_EMAIL_CODE);
}- RedisService.getValues가 키 미존재 시 null을 반환하도록 수정
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public String getStoredCode(String email) { | |
| String key = EMAIL_KEY_PREFIX + email; | |
| return redisService.getValues(key); | |
| } | |
| public void checkAuthCode(CodeCheckRequest request) { | |
| String storedCode = getStoredCode(request.getEmail()); | |
| if ("false".equals(storedCode)) { | |
| throw new BusinessException(ErrorCode.EXPIRED_EMAIL_CODE); | |
| } | |
| // ... the rest of the method remains unchanged ... | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/WhoIsRoom/WhoIs_Server/domain/auth/service/MailService.java
around lines 151-154, getStoredCode() currently returns the string "false" when
a Redis key is missing, causing downstream null checks in checkAuthCode to never
detect expiration; update the code so that either 1) change the null check in
the auth-code validation to explicitly check for "false" (e.g., treat a returned
"false" as expired) or 2) alter RedisService.getValues to return null when a key
does not exist and keep the existing null check—pick one approach and make
corresponding adjustments so expired codes trigger the EXPIRED_EMAIL_CODE path
correctly.
| config: | ||
| activate: | ||
| on-profile: common | ||
| import: optional:file:.env[.properties] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
.env 파일 임포트 확인 권장
환경 변수를 외부화하는 좋은 방식입니다. optional: 접두사로 파일이 없어도 애플리케이션이 시작되도록 처리했네요.
.env 파일에 MAIL_ADDRESS, MAIL_PASSWORD 등 필요한 변수가 모두 정의되어 있는지 확인해보세요.
🏁 Script executed:
#!/bin/bash
# .env 파일이 존재하는지, 필수 메일 관련 변수가 정의되어 있는지 확인
if [ -f .env ]; then
echo "=== .env 파일 발견 ==="
echo "MAIL_ADDRESS 정의 여부:"
grep -E "^MAIL_ADDRESS=" .env || echo "❌ MAIL_ADDRESS 미정의"
echo "MAIL_PASSWORD 정의 여부:"
grep -E "^MAIL_PASSWORD=" .env || echo "❌ MAIL_PASSWORD 미정의"
else
echo "⚠️ .env 파일이 루트 디렉토리에 없습니다."
echo "spring.config.import가 optional이므로 애플리케이션은 실행되지만 메일 기능은 작동하지 않을 수 있습니다."
fiLength of output: 98
.env 파일 추가 또는 환경 변수 주입 방안 마련 필요
현재 리포지토리에 .env 파일이 없어 optional:file:.env[.properties] 설정만으로는 MAIL_ADDRESS, MAIL_PASSWORD 등 메일 관련 환경 변수가 주입되지 않아 메일 기능이 동작하지 않습니다.
반드시 루트에 .env 파일을 추가하거나, CI/CD 또는 운영 환경에서 해당 변수를 주입할 수 있도록 설정 문서 및 자동화 스크립트를 업데이트하세요.
🤖 Prompt for AI Agents
src/main/resources/application.yml around line 62: the import line references an
optional .env file but there is no .env in the repo so MAIL_ADDRESS,
MAIL_PASSWORD and other mail env vars are not being injected; either add a .env
file at the repository root with the required MAIL_* entries, or update CI/CD
and deployment scripts to inject those variables (export/set them in pipeline
secrets or environment configuration) and update the README/deployment docs to
list required variables and how to provide them.
yskim6772
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다-!
Related issue 🛠
Work Description 📝
메일 인증번호 기능
비밀번호 수정 기능
Jwt 관련
Screenshot 📸
Uncompleted Tasks 😅
To Reviewers 📢
Summary by CodeRabbit
신기능
변경 사항
운영