-
Notifications
You must be signed in to change notification settings - Fork 2
[BE] SISC1-150 [FEAT] 회원정보 관리 및 로그아웃 구현 #62
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
The head ref may contain hidden characters: "SISC1-150-BE-\uD68C\uC6D0\uC815\uBCF4-\uAD00\uB9AC"
Conversation
Walkthrough로그아웃 엔드포인트 및 RefreshToken 엔티티/리포지토리를 추가하고, LoginService.logout 구현과 사용자 정보 수정(PATCH /user/{userId}) 기능 및 관련 DTO·서비스를 도입합니다. 관련 유닛 테스트들이 추가되어 흐름과 엣지 케이스를 검증합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant OauthLoginController
participant LoginService
participant JwtParser
participant RefreshTokenRepository
Client->>OauthLoginController: POST /auth/logout (Authorization: Bearer {token})
OauthLoginController->>OauthLoginController: Authorization 헤더 검증 및 토큰 추출
OauthLoginController->>LoginService: logout(accessToken)
LoginService->>JwtParser: parse accessToken -> userId
LoginService->>RefreshTokenRepository: deleteByUserId(userId)
RefreshTokenRepository-->>LoginService: 삭제 완료
LoginService-->>OauthLoginController: 완료
OauthLoginController->>OauthLoginController: refresh token 쿠키 삭제(maxAge=0)
OauthLoginController-->>Client: 200 OK + 성공 메시지
sequenceDiagram
participant Client
participant UserController
participant UserService
participant UserRepository
participant PasswordEncoder
Client->>UserController: PATCH /user/{userId} (UserUpdateRequest)
UserController->>UserService: updateUser(userId, request)
UserService->>UserRepository: findById(userId)
alt 사용자 존재
UserService->>UserService: name/phone 업데이트(제공된 필드만)
alt password 제공
UserService->>PasswordEncoder: encode(password)
PasswordEncoder-->>UserService: encodedPassword
UserService->>UserRepository: save(updatedUser)
else
UserService->>UserRepository: save(updatedUser)
end
UserRepository-->>UserService: 저장 완료
UserService-->>UserController: 성공
else 사용자 없음
UserService-->>UserController: 예외(사용자 없음)
end
UserController-->>Client: 200 OK 또는 에러
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
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 (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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (7)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java (1)
3-3: 사용되지 않는 import를 제거하세요.
HttpServletRequest가 import되었지만 인터페이스에서 사용되지 않습니다.-import jakarta.servlet.http.HttpServletRequest;backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)
329-360: 모든 필드가 null일 때 save() 호출이 필요한지 검토하세요.Line 358에서 모든 필드가 null이어도
userRepository.save(existingUser)가 호출됩니다. 변경사항이 없을 때는 save를 호출하지 않는 것이 더 효율적일 수 있습니다.서비스 구현부에서 변경사항이 있을 때만 save를 호출하도록 개선을 고려해보세요:
// 서비스 구현 예시 public void updateUser(UUID userId, UserUpdateRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("User not found")); boolean hasChanges = false; if (request.getUsername() != null) { user.setName(request.getUsername()); hasChanges = true; } // ... other fields if (hasChanges) { userRepository.save(user); } }backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (3)
3-3: 사용되지 않는 import를 제거하세요.
HttpServletRequest가 import되었지만 사용되지 않습니다.-import jakarta.servlet.http.HttpServletRequest;
22-22: 사용되지 않는 import를 제거하세요.
java.sql.Ref가 import되었지만 사용되지 않습니다.-import java.sql.Ref;
60-65: 로그아웃 구현에서 예외 처리를 추가하세요.
jwtParser.getUserIdFromToken(accessToken)이 잘못된 토큰으로 예외를 발생시킬 수 있습니다. 이미 로그아웃된 사용자나 만료된 토큰의 경우에도 graceful하게 처리하는 것이 좋습니다.@Override public void logout(String accessToken) { + try { UUID userId = jwtParser.getUserIdFromToken(accessToken); refreshTokenRepository.deleteByUserId(userId); log.info("로그아웃 완료: userId={}", userId); + } catch (Exception e) { + log.warn("로그아웃 처리 중 토큰 파싱 실패: {}", e.getMessage()); + // 이미 만료되거나 유효하지 않은 토큰도 로그아웃 성공으로 간주 + } }backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
109-109: 불필요한 save() 호출 제거 가능메서드에
@Transactional을 추가하면 JPA의 더티 체킹(dirty checking)이 자동으로 변경사항을 감지하여 저장하므로 명시적인save()호출이 불필요합니다.
@Transactional을 추가한 후 다음과 같이 수정할 수 있습니다:- userRepository.save(user); + // @Transactional에 의해 자동으로 변경사항이 저장됨backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1)
9-11: 입력 검증 어노테이션 추가 권장DTO에 Bean Validation 어노테이션을 추가하여 컨트롤러 레벨에서 입력 값을 검증하는 것이 좋습니다. 특히 비밀번호는 강도 요구사항이 있을 수 있고, 전화번호는 형식 검증이 필요합니다.
다음과 같이 개선할 수 있습니다:
+import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + @Getter @Setter public class UserUpdateRequest { + @Size(min = 2, max = 50, message = "이름은 2자 이상 50자 이하여야 합니다") private String username; + + @Pattern(regexp = "^[0-9]{10,11}$", message = "올바른 전화번호 형식이 아닙니다") private String phoneNumber; + + @Pattern( + regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$", + message = "비밀번호는 8자 이상, 영문, 숫자, 특수문자를 포함해야 합니다" + ) private String password; // 변경 시에만 받기 }컨트롤러에서
@Valid어노테이션을 추가하는 것도 잊지 마세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java(3 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/Oauth2Service.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(3 hunks)backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java(3 hunks)backend/src/test/java/org/sejongisc/backend/auth/service/LoginServiceImplTest.java(4 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
Slf4j(23-199)backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
Slf4j(28-110)
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
RefreshToken(7-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Backend Gate
🔇 Additional comments (10)
backend/src/main/java/org/sejongisc/backend/auth/service/Oauth2Service.java (1)
10-11: LGTM!공백 추가만 있고 기능적 변경은 없습니다.
backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java (1)
10-10: LGTM!로그아웃 메서드 시그니처가 올바르게 선언되었습니다.
backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (2)
273-308: LGTM!회원정보 수정 성공 케이스를 올바르게 테스트하고 있습니다. 이름, 전화번호, 비밀번호 변경이 모두 검증되고 있습니다.
310-327: LGTM!존재하지 않는 사용자에 대한 예외 처리를 올바르게 테스트하고 있습니다.
backend/src/main/java/org/sejongisc/backend/user/service/UserService.java (1)
5-5: LGTM!
updateUser메서드가 인터페이스에 올바르게 선언되었습니다.Also applies to: 9-9, 16-16
backend/src/test/java/org/sejongisc/backend/auth/service/LoginServiceImplTest.java (2)
143-169: LGTM!OAuth 로그인 사용자(passwordHash가 null)가 일반 로그인을 시도할 때 예외가 발생하는 것을 올바르게 테스트하고 있습니다.
172-188: LGTM!로그아웃 플로우가 올바르게 테스트되고 있습니다. JwtParser를 통한 userId 추출과 RefreshTokenRepository를 통한 토큰 삭제가 검증되고 있습니다.
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
14-14: 토큰 길이 제한 검토 필요JWT 토큰의 길이는 payload 크기에 따라 달라질 수 있습니다. 500자가 충분한지 확인이 필요합니다. 일반적으로 JWT 토큰은 200-500자 정도이지만, 클레임이 많아지면 더 길어질 수 있습니다.
실제 생성되는 RefreshToken의 길이를 확인하고, 여유를 두어 1000자 정도로 설정하는 것을 권장합니다:
- @Column(nullable = false, length = 500) + @Column(nullable = false, length = 1000) private String token;backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java (2)
242-260: OAuth 초기화 테스트 구현 우수여러 OAuth 제공자(Google, Kakao, GitHub)에 대한 초기화 엔드포인트를 효율적으로 테스트하고 있습니다.
containsString을 사용하여 각 제공자의 인증 URL이 올바르게 포함되는지 검증하는 것은 좋은 접근 방식입니다.
309-332: 로그아웃 테스트 커버리지 우수로그아웃 기능에 대한 테스트가 잘 작성되었습니다:
- Line 320:
verify를 사용하여loginService.logout()이 정확히 한 번 호출되었는지 확인- Line 331: Authorization 헤더가 없을 때
logout()이 호출되지 않았는지 확인이러한 검증은 보안상 중요한 부분으로, 올바른 테스트 방식입니다.
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java
Outdated
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/dto/UserUpdateRequest.java
Outdated
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
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
discipline24
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.
GPT 냄새가 여기저기서 나는데 조금 신경써주세요~
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: 3
♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
17-17: 필드명을 camelCase로 변경 필요 (재확인)이전 리뷰에서 지적된 필드명
userid가 아직 수정되지 않았습니다. Java 네이밍 컨벤션에 따라userId로 변경해야 합니다.또한
RefreshTokenRepository의 메서드명(findByUserid,deleteByUserId)에 일관성이 없으므로 함께 수정이 필요합니다.다음과 같이 수정하세요:
@Id @Column(name = "user_id", nullable = false, columnDefinition = "uuid") - private UUID userid; + private UUID userId;
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
7-21: 토큰 만료 시간 필드 추가 고려RefreshToken에 만료 시간을 저장하는
expiresAt또는createdAt필드를 추가하면 토큰 생명주기 관리가 용이해집니다. 이를 통해 만료된 토큰을 주기적으로 정리하거나 검증 시 활용할 수 있습니다.예시:
@Column(name = "expires_at", nullable = false) private LocalDateTime expiresAt;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java(3 hunks)backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java(3 hunks)backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(4 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
Slf4j(24-213)backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
Slf4j(28-110)
🔇 Additional comments (7)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)
53-53: LGTM!입력값 검증을 위한 에러 코드가 적절하게 추가되었습니다. 기존 패턴과 일관성이 있습니다.
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
177-210: LGTM!로그아웃 엔드포인트가 잘 구현되었습니다:
- 헤더 검증이 적절히 수행됩니다
- 예외 처리를 통해 멱등성이 보장됩니다 (이미 만료된 토큰도 200 OK 반환)
- Refresh Token 쿠키가 항상 삭제됩니다
이전 리뷰의 피드백이 모두 반영되었습니다.
backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1)
1-12: LGTM!DTO 필드명이 엔티티와 일치하며 구조가 명확합니다. 이전 리뷰에서 제기된 필드명 불일치 문제가 해결되었습니다.
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (1)
60-66: LGTM!로그아웃 메서드가 적절히 구현되었습니다:
@Transactional어노테이션이 적용되어 트랜잭션 안전성이 보장됩니다- JWT에서 userId를 추출하고 refresh token을 삭제하는 로직이 명확합니다
- 로그도 적절히 기록됩니다
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)
51-69: LGTM!회원정보 수정 엔드포인트가 안전하게 구현되었습니다:
@AuthenticationPrincipal을 통한 인증 확인- 본인 확인 로직으로 권한 검증 (Lines 62-65)
@Valid어노테이션으로 입력값 검증이전 리뷰에서 제기된 인증/인가 보안 취약점이 모두 해결되었습니다.
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
100-126: LGTM!회원정보 수정 메서드가 견고하게 구현되었습니다:
@Override와@Transactional어노테이션이 적절히 적용되었습니다CustomException을 사용하여 예외 처리 일관성을 유지합니다 (Line 104)- 모든 필드에 대해
trim()과isEmpty()검증을 수행합니다 (Lines 107-121)- 비밀번호는 유효한 경우에만 인코딩하여 저장합니다
이전 리뷰에서 제기된 모든 문제(필수 어노테이션 누락, 예외 처리 불일치, 입력 검증 부재)가 해결되었습니다.
backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)
273-354: LGTM!회원정보 수정 기능에 대한 테스트가 포괄적으로 작성되었습니다:
- 성공 케이스: 모든 필드 업데이트 검증 (Lines 273-308)
- 실패 케이스: 존재하지 않는 사용자 예외 처리 검증 (Lines 310-321)
- 엣지 케이스: 모든 필드가 null일 때 변경 없음 검증 (Lines 323-354)
Mock 상호작용과 assertion이 적절하며, 기존 테스트 패턴과 일관성이 있습니다.
구현 내용
controller
service
repository (dao)
Summary by CodeRabbit
새로운 기능
에러/검증
테스트