Skip to content

[feat] #15 - 토큰 재발급, 로그아웃, 탈퇴 기능 구현 - #18

Merged
Jy000n merged 34 commits into
developfrom
feat/#15-reissue-logout-withdraw
Jul 7, 2026
Merged

Jy000n merged 34 commits into
developfrom
feat/#15-reissue-logout-withdraw

Conversation

@Jy000n

@Jy000n Jy000n commented Jul 6, 2026 •

Copy link
Copy Markdown
Member

관련 이슈 🛠

작업 내용 요약 ✏️

로그인 이후 인증 관련 API 구현
RefreshToken 기반 AccessToken 재발급, 로그아웃, 회원 탈퇴 기능 제공

주요 변경 사항 🛠️

  • [Auth]: POST /api/v1/auth/reissue : refreshToken + sessionId 쿠키 검증 후 새 accessToken 발급 (refreshToken rotation 적용)
  • [Auth]: POST /api/v1/auth/logout : 블랙리스트 등록 + Redis RefreshToken 삭제 + 쿠키 만료 처리
  • [Auth]: DELETE /api/v1/auth/withdraw : 블랙리스트 등록 + Redis 삭제 + DB 유저 삭제 + 쿠키 만료 처리
  • [Auth]: BlacklistService : 로그아웃/탈퇴된 accessToken Redis 블랙리스트 관리
  • [Auth]: CookieUtil : 쿠키 생성/만료 공통 유틸 추가
  • [Auth] AuthControllerDocs 추가 및 Swagger 문서화 적용
  • [Auth]: AuthTokenRequest, ReissueResponse DTO 추가 (Map 타입 제거, @NotBlank 검증 추가)
  • [Auth]: TokenExtractor : Authorization 헤더에서 토큰 추출 유틸 추가
  • [JWT]: JwtAuthenticationFilter : 블랙리스트 검증 추가

트러블 슈팅 ⚽️

  • 쿠키 path 설정 문제로 재발급/로그아웃 API에 쿠키 미전송
    • 쿠키 path=/auth로 설정했으나 실제 API 경로가 /api/v1/auth/reissue라 쿠키가 전송되지 않는 문제 발생
      • 쿠키 path는 해당 경로로 시작하는 요청에만 쿠키를 전송하는 규칙이 있어 /api/v1/auth는 /auth로 시작하지 않기 때문
      • 탈퇴 API가 /api/v1/users로 되어있어 인증 관련 API 경로가 일관성이 없는 문제도 있었음
    • 탈퇴 API 경로를 /api/v1/auth/withdraw로 변경하여 인증 관련 API를 /api/v1/auth 하위로 통일 + 쿠키 path도 /api/v1/auth로 맞춰 해결

테스트 결과 📄

  • POST /api/v1/auth/reissue : 새 accessToken 발급 및 refreshToken rotation 확인
  • POST /api/v1/auth/reissue : 만료된 refreshToken으로 요청 시 401 확인
  • POST /api/v1/auth/logout : 200 응답 및 쿠키 만료 확인
  • POST /api/v1/auth/logout : 로그아웃 후 같은 accessToken으로 요청 시 401 확인
  • DELETE /api/v1/auth/withdraw : 200 응답 확인
  • DELETE /api/v1/auth/withdraw : DB users 테이블에서 유저 삭제 확인
  • DELETE /api/v1/auth/withdraw : 탈퇴 후 같은 accessToken으로 요청 시 401 확인

스크린샷 📷 (테스트 진행 중)

  • POST /api/v1/auth/reissue : 새 accessToken 발급 및 refreshToken rotation 확인
image
  • POST /api/v1/auth/reissue : 만료된 refreshToken으로 요청 시 401 확인
image
  • POST /api/v1/auth/logout : 로그아웃 200 응답 및 쿠키 만료 확인
image
  • POST /api/v1/auth/logout : 로그아웃 후 같은 accessToken으로 요청 시 401 확인
image
  • DELETE /api/v1/auth/withdraw : 탈퇴 200 응답 확인
image
  • DELETE /api/v1/auth/withdraw : DB users 테이블에서 유저 삭제 확인
image
  • DELETE /api/v1/auth/withdraw : 탈퇴 후 같은 accessToken으로 요청 시 401 확인
image

리뷰 요구사항 📢

📎 참고 자료 (선택)

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • 인증 API 입력을 개선했고, 토큰 재발급/로그아웃/회원 탈퇴 엔드포인트를 추가·정비했습니다.
    • 접근 토큰 블랙리스트 기반으로 로그아웃/탈퇴 후 무효 처리 기능을 도입했습니다.
    • 리프레시 토큰·세션 쿠키 발급/만료를 공통 규칙으로 통합했습니다.
  • Bug Fixes

    • 인증 성공/오류 코드가 통일되었고, 에러 응답은 UTF-8로 직렬화해 반환하도록 개선했습니다.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jy000n, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: adf0b876-8e5a-4a52-9d6f-49172b92be92

📥 Commits

Reviewing files that changed from the base of the PR and between be4cc5c and 5f46700.

📒 Files selected for processing (1)
  • src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java

Walkthrough

토큰 재발급, 로그아웃, 회원 탈퇴 API가 추가되고, 인증 필터가 블랙리스트를 검사하도록 바뀌었습니다. 쿠키 생성/만료 유틸과 토큰 추출 유틸이 신설됐고, 에러·성공 코드와 응답 형식, OAuth 쿠키 발급 방식도 함께 조정되었습니다.

Changes

인증 API 확장

Layer / File(s) Summary
공용 유틸리티
src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java, src/main/java/com/Timo/Timo/global/auth/utils/TokenExtractor.java
ResponseCookie 생성/만료 유틸과 Authorization 헤더에서 Bearer 토큰을 추출하는 유틸 클래스가 추가되었습니다.
블랙리스트 및 만료 조회
src/main/java/com/Timo/Timo/global/auth/service/BlackListService.java, src/main/java/com/Timo/Timo/global/jwt/provider/JwtTokenProvider.java
Redis 기반 액세스 토큰 블랙리스트 서비스와 토큰의 남은 만료 시간을 계산하는 메서드가 추가되었습니다.
AuthService: reissue/logout/withdraw 로직
src/main/java/com/Timo/Timo/global/auth/dto/ReissueResult.java, src/main/java/com/Timo/Timo/global/auth/service/AuthService.java, src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java
ReissueResult DTO와 reissue, logout, withdraw 메서드가 추가되어 리프레시 토큰 검증/재발급, 블랙리스트 등록, 세션 삭제, 사용자 삭제를 처리합니다.
AuthController와 응답 팩토리
src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java, src/main/java/com/Timo/Timo/global/auth/factory/AuthResponseFactory.java, src/main/java/com/Timo/Timo/global/auth/dto/request/AuthTokenRequest.java, src/main/java/com/Timo/Timo/global/auth/dto/response/AuthReissueResponse.java, src/main/java/com/Timo/Timo/global/auth/dto/response/AuthTokenResponse.java
/token 입력 DTO 변경과 함께 /reissue, /logout, /withdraw 엔드포인트가 추가되고, 응답 생성이 AuthResponseFactory로 위임됩니다.
JwtAuthenticationFilter 블랙리스트 검사
src/main/java/com/Timo/Timo/global/jwt/filter/JwtAuthenticationFilter.java
TokenExtractor로 토큰을 추출하고 BlackListService로 블랙리스트 여부를 확인하도록 인증 조건이 강화되고, 내부 토큰 파싱 메서드는 제거되었습니다.
에러/성공 코드 및 응답 형식 정리
src/main/java/com/Timo/Timo/global/auth/exception/AuthErrorCode.java, src/main/java/com/Timo/Timo/global/auth/exception/AuthSuccessCode.java, src/main/java/com/Timo/Timo/global/auth/handler/OAuthSuccessHandler.java, src/main/java/com/Timo/Timo/global/auth/handler/AuthErrorResponseWriter.java, src/main/java/com/Timo/Timo/global/common/BaseTimeEntity.java, src/main/java/com/Timo/Timo/global/exception/dto/ErrorDto.java, src/main/java/com/Timo/Timo/global/auth/handler/JwtAuthenticationEntryPoint.java, src/main/java/com/Timo/Timo/global/auth/principal/CustomUserDetails.java
에러/성공 코드 문자열이 통일되고, OAuthSuccessHandler가 CookieUtil을 사용해 쿠키를 발급하도록 변경되며, 에러 응답과 시간 필드의 JSON/OpenAPI 형식이 조정됩니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthController
    participant AuthService
    participant RefreshTokenService
    participant BlackListService
    participant JwtTokenProvider

    Client->>AuthController: POST /reissue (refreshToken, sessionId cookies)
    AuthController->>AuthService: reissue(refreshToken, sessionId)
    AuthService->>RefreshTokenService: refreshToken/sessionId 검증
    RefreshTokenService-->>AuthService: 검증 결과
    AuthService->>RefreshTokenService: 기존 refreshToken 삭제
    AuthService->>RefreshTokenService: 새 refreshToken/sessionId 저장
    AuthService-->>AuthController: ReissueResult(accessToken, refreshToken, sessionId)
    AuthController-->>Client: accessToken + Set-Cookie(refreshToken, sessionId)

    Client->>AuthController: POST /logout or DELETE /withdraw
    AuthController->>AuthController: TokenExtractor.resolveToken(request)
    AuthController->>AuthService: logout/withdraw(accessToken, userId, sessionId)
    AuthService->>JwtTokenProvider: getRemainingExpiry(accessToken)
    AuthService->>BlackListService: addToBlacklist(accessToken, remainingExpiry)
    AuthService->>RefreshTokenService: sessionId의 refreshToken 삭제
    AuthController-->>Client: 성공 응답 + 쿠키 만료
Loading

Possibly related PRs

  • Team-Timo/Timo-Server#14: AuthController의 /api/v1/auth/token 교환 흐름을 도입한 선행 변경으로, 이번 PR의 /token 입력 DTO 변경과 직접 연결됩니다.

Suggested reviewers: laura-jung, aneykrap

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 재발급과 로그아웃은 반영됐지만, 회원 탈퇴가 요구된 /api/v1/user가 아니라 /api/v1/auth/withdraw로 보여 이슈 조건을 충족하지 않습니다. 회원 탈퇴 엔드포인트를 요구된 /api/v1/user 경로로 맞추고 관련 문서/호출부도 함께 정리하세요.
Out of Scope Changes check ⚠️ Warning BaseTimeEntity, ErrorDto, 일부 코드값/포맷 변경 등은 요청된 재발급·로그아웃·탈퇴 API 범위를 벗어납니다. 비관련 직렬화/문서화/코드값 정리는 별도 PR로 분리하고 이번 PR은 인증 API와 블랙리스트에만 집중하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 토큰 재발급, 로그아웃, 회원 탈퇴 구현이라는 핵심 변경을 정확히 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#15-reissue-logout-withdraw

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java (1)

8-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

createCookie/expireCookie 중복 로직 제거 제안

두 메서드가 httpOnly, secure, path, sameSite 설정을 그대로 중복하고 있습니다. expireCookie는 createCookie(name, "", 0, secure) 호출로 대체할 수 있어 속성 값이 어긋날 위험을 줄일 수 있습니다.

♻️ 제안 diff
   public static ResponseCookie expireCookie(String name, boolean secure) {
-    return ResponseCookie.from(name, "")
-        .httpOnly(true)
-        .secure(secure)
-        .path("/api/v1/auth")
-        .maxAge(0)
-        .sameSite("Strict")
-        .build();
+    return createCookie(name, "", 0, secure);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java` around lines 8
- 26, `CookieUtil`의 `createCookie`와 `expireCookie`에서 `httpOnly`, `secure`,
`path`, `sameSite` 설정이 중복되므로, `expireCookie`는 `createCookie(name, "", 0,
secure)`를 호출하도록 변경해 중복을 제거하고 두 메서드의 쿠키 속성이 항상 일치하도록 정리하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/Timo/Timo/global/auth/service/AuthService.java`:
- Around line 93-107: `AuthService.withdraw`의 순서와 트랜잭션 경계를 조정해야 합니다. 먼저
`userRepository.findById(userId)`로 사용자 존재 여부를 확인해 `USER_NOT_FOUND`가 나면 즉시 중단하고,
그 다음에 `blacklistService.addToBlacklist`와
`refreshTokenService.deleteRefreshToken` 같은 부수효과를 수행하도록 `withdraw` 흐름을 바꾸세요. 또한
`withdraw`에 `@Transactional`을 적용해 `userRepository.delete(user)`까지의 DB 작업이 하나의 경계
안에서 처리되도록 하고, `accessToken`과 `sessionId`의 null 처리도 현재처럼 유지해 예외 없는 경우만 부수효과가 실행되게
하세요.

In `@src/main/java/com/Timo/Timo/global/auth/service/BlackListService.java`:
- Around line 16-23: `BlackListService.addToBlacklist` should skip Redis writes
when `remainingExpiry` is zero or negative, because
`redisTemplate.opsForValue().set(..., TimeUnit.SECONDS)` cannot use an invalid
TTL. Add an early return guard at the start of `addToBlacklist(String
accessToken, long remainingExpiry)` so only positive TTL values are stored,
keeping the blacklist flow safe for near-expiry tokens.

---

Nitpick comments:
In `@src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java`:
- Around line 8-26: `CookieUtil`의 `createCookie`와 `expireCookie`에서 `httpOnly`,
`secure`, `path`, `sameSite` 설정이 중복되므로, `expireCookie`는 `createCookie(name, "",
0, secure)`를 호출하도록 변경해 중복을 제거하고 두 메서드의 쿠키 속성이 항상 일치하도록 정리하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e7e08e9-329c-4423-bc3f-37575ca67938

📥 Commits

Reviewing files that changed from the base of the PR and between cfa847a and 7aa87d0.

📒 Files selected for processing (15)
  • src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java
  • src/main/java/com/Timo/Timo/global/auth/dto/ReissueResult.java
  • src/main/java/com/Timo/Timo/global/auth/dto/response/AuthTokenResponse.java
  • src/main/java/com/Timo/Timo/global/auth/exception/AuthErrorCode.java
  • src/main/java/com/Timo/Timo/global/auth/exception/AuthSuccessCode.java
  • src/main/java/com/Timo/Timo/global/auth/handler/AuthErrorResponseWriter.java
  • src/main/java/com/Timo/Timo/global/auth/handler/JwtAuthenticationEntryPoint.java
  • src/main/java/com/Timo/Timo/global/auth/handler/OAuthSuccessHandler.java
  • src/main/java/com/Timo/Timo/global/auth/principal/CustomUserDetails.java
  • src/main/java/com/Timo/Timo/global/auth/service/AuthService.java
  • src/main/java/com/Timo/Timo/global/auth/service/BlackListService.java
  • src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java
  • src/main/java/com/Timo/Timo/global/auth/utils/TokenExtractor.java
  • src/main/java/com/Timo/Timo/global/jwt/filter/JwtAuthenticationFilter.java
  • src/main/java/com/Timo/Timo/global/jwt/provider/JwtTokenProvider.java
💤 Files with no reviewable changes (3)
  • src/main/java/com/Timo/Timo/global/auth/dto/response/AuthTokenResponse.java
  • src/main/java/com/Timo/Timo/global/auth/handler/AuthErrorResponseWriter.java
  • src/main/java/com/Timo/Timo/global/auth/principal/CustomUserDetails.java

Comment thread src/main/java/com/Timo/Timo/global/auth/service/AuthService.java

@laura-jung laura-jung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blacklist 구현까지 잘 되어있는 것 같습니다. 코멘트 남겨두었으니 확인 부탁드립니다.

.accessToken(result.getAccessToken())
.build();

return ResponseEntity.ok()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3) 현재 컨트롤러에서 AuthReissueResponse 생성뿐 아니라 refreshToken/sessionId 쿠키 생성, Set-Cookie 헤더 설정, cache-control 설정까지 함께 처리하고 있어 응답 조립 책임이 조금 큰 것 같습니다.

비즈니스 로직은 authService.reissue()에서 처리하고 있으니, 쿠키와 ResponseEntity를 조립하는 부분은 AuthResponseFactory나 별도 helper로 분리하면 컨트롤러가 더 얇아지고 다른 인증 응답에서도 재사용하기 좋을 것 같아요!

컨트롤러에서의 로직은 최소한으로 가져가는 것이 좋습니다.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해주신 대로 쿠키 생성 및 Set-Cookie 헤더 설정 로직을 AuthResponseFactory로 분리했습니다. 이를 통해 reissue, logout, withdraw에서 거의 동일하게 반복되던 처리 로직을 공통화하여 재사용성을 높였습니다.

@PostMapping("/token")
public ResponseEntity<BaseResponse<AuthTokenResponse>> token(
@RequestBody Map<String, String> body
@RequestBody AuthTokenRequest request

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2) AuthTokenRequest.code에 @NotBlank가 추가된 점은 좋은데, 컨트롤러의 @RequestBody 파라미터에 @Valid가 없어 실제 검증이 실행되지 않을 수 있을 것 같습니다.

@Valid @RequestBody AuthTokenRequest request로 변경해야 빈 문자열/공백 code가 서비스 레이어로 넘어가기 전에 차단될 것 같아요.

@Jy000n Jy000n Jul 7, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해주신 내용 반영했습니다! @Valid를 추가하여 요청 단계에서 code 값 검증이 수행되도록 수정했습니다. 감사합니다-!!

@aneykrap aneykrap left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이제 로그인쪽이 거의 끝을 향해 달려가네용 파이팅입니다!!

}

@Transactional
public void withdraw(String accessToken, Long userId, String sessionId) {

@aneykrap aneykrap Jul 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2) 회원 탈퇴 시 현재 요청의 sessionId에 해당하는 refresh token만 삭제하고 있어 같은 사용자의 다른 세션 refresh token은 Redis에 남을 수 있을 것 같아요 그리고 reissue()에서는 user 존재 여부를 확인하지 않고 있어서 탈퇴 후 남아 있는 refresh token으로 access token이 재발급될 가능성이 있지 않을까 싶습니당 탈퇴 시 해당 userId의 모든 refresh token을 삭제하거나 reissue()에서 사용자 존재 여부를 검증하는 처리가 필요하지 않을까 싶어요!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러네요 동의합니다..!! 말씀해주신 두 가지 내용 반영했습니다!!

withdraw(): 현재 세션 토큰만 지우던 걸 refreshTokenService.deleteAllRefreshTokens(userId)로 바꿔서 해당 유저의 모든 세션을 정리하도록 했습니다.
reissue(): userRepository.existsById(userId) 체크를 추가해서, 혹시 다른 경로로 refresh token이 살아있어도 이미 탈퇴한 유저면 재발급이 막히도록 했습니다.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/com/Timo/Timo/global/auth/factory/AuthResponseFactory.java (1)

51-56: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

로그아웃/탈퇴 응답에 Cache-Control: no-store를 추가하는 것을 고려해 보세요.

tokenResponse/reissueResponse는 no-store를 설정하지만, 쿠키 만료 응답인 expiredCookieResponse에는 없습니다. 인증 관련 Set-Cookie 응답이 캐시되지 않도록 일관되게 헤더를 부여하는 편이 안전합니다.

♻️ 제안
     return ResponseEntity.ok()
         .header(HttpHeaders.SET_COOKIE, CookieUtil.expireCookie("refreshToken", cookieSecure).toString())
         .header(HttpHeaders.SET_COOKIE, CookieUtil.expireCookie("sessionId", cookieSecure).toString())
+        .header("Cache-Control", "no-store")
         .body(BaseResponse.onSuccess(successCode, null));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/Timo/Timo/global/auth/factory/AuthResponseFactory.java`
around lines 51 - 56, `expiredCookieResponse(AuthSuccessCode)` is missing the
same cache-prevention header used by `tokenResponse` and `reissueResponse`.
Update this method in `AuthResponseFactory` to add `Cache-Control: no-store`
alongside the `Set-Cookie` headers so logout/withdrawal cookie-expiration
responses are handled consistently and not cached.
src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java (1)

40-45: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

redisTemplate.keys()는 프로덕션에서 Redis를 블로킹할 수 있습니다.

keys(...)는 내부적으로 Redis KEYS 명령을 사용하며, 전체 키스페이스를 O(N)으로 순회하는 동안 서버가 블로킹됩니다. 사용자별 세션 수가 늘어나고 전체 키가 많아질수록 탈퇴 요청마다 다른 명령까지 지연될 수 있습니다. SCAN 기반 순회(예: ScanOptions)로 교체하는 것을 권장합니다. As per path instructions, "성능 문제나 불필요한 중복 로직이 있는지 확인해 주세요."

♻️ SCAN 기반 예시
public void deleteAllRefreshTokens(String userId) {
  ScanOptions options = ScanOptions.scanOptions()
      .match(KEY_PREFIX + userId + ":*")
      .count(100)
      .build();
  try (Cursor<byte[]> cursor = redisTemplate.executeWithStickyConnection(
      conn -> conn.keyCommands().scan(options))) {
    List<String> keys = new ArrayList<>();
    while (cursor.hasNext()) {
      keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
    }
    if (!keys.isEmpty()) {
      redisTemplate.delete(keys);
    }
  }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java`
around lines 40 - 45, `RefreshTokenService.deleteAllRefreshTokens` currently
uses `redisTemplate.keys(...)`, which can block Redis in production because it
scans the full keyspace. Replace this with a `SCAN`-based lookup using
`ScanOptions` and the existing `redisTemplate` to iterate matching refresh-token
keys for the given userId, collect them, and delete them only if any are found.
Keep the change localized to `deleteAllRefreshTokens` and preserve the
`KEY_PREFIX` pattern while removing the direct `keys()` usage.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java`:
- Around line 36-37: Remove the unused cookieSecure field from AuthController,
since cookie handling now lives in AuthResponseFactory and this injected setting
is no longer referenced. Update AuthController to drop the
`@Value`("${app.auth.cookie-secure}") injection and any related imports or
constructor/field usage, keeping the cookie security configuration only in the
new response factory path.

---

Nitpick comments:
In `@src/main/java/com/Timo/Timo/global/auth/factory/AuthResponseFactory.java`:
- Around line 51-56: `expiredCookieResponse(AuthSuccessCode)` is missing the
same cache-prevention header used by `tokenResponse` and `reissueResponse`.
Update this method in `AuthResponseFactory` to add `Cache-Control: no-store`
alongside the `Set-Cookie` headers so logout/withdrawal cookie-expiration
responses are handled consistently and not cached.

In `@src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java`:
- Around line 40-45: `RefreshTokenService.deleteAllRefreshTokens` currently uses
`redisTemplate.keys(...)`, which can block Redis in production because it scans
the full keyspace. Replace this with a `SCAN`-based lookup using `ScanOptions`
and the existing `redisTemplate` to iterate matching refresh-token keys for the
given userId, collect them, and delete them only if any are found. Keep the
change localized to `deleteAllRefreshTokens` and preserve the `KEY_PREFIX`
pattern while removing the direct `keys()` usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3756d520-a9d4-444c-82e8-875dcfde388f

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5fcfb and be4cc5c.

⛔ Files ignored due to path filters (1)
  • src/main/java/com/Timo/Timo/global/auth/docs/AuthControllerDocs.java is excluded by !**/docs/**
📒 Files selected for processing (5)
  • src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java
  • src/main/java/com/Timo/Timo/global/auth/dto/request/AuthTokenRequest.java
  • src/main/java/com/Timo/Timo/global/auth/factory/AuthResponseFactory.java
  • src/main/java/com/Timo/Timo/global/auth/service/AuthService.java
  • src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/Timo/Timo/global/auth/dto/request/AuthTokenRequest.java

Comment thread src/main/java/com/Timo/Timo/global/auth/controller/AuthController.java Outdated

@aneykrap aneykrap left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 리뷰 반영된거 다 확인했습니다!! 고생 많았어용 자윤이!!!

@laura-jung laura-jung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어푸 드립니다. 이제 머지합니다아!!!

@github-actions github-actions Bot added the slack-approval-notified Slack 승인 완료 알림 중복 방지용 라벨 label Jul 7, 2026
@Jy000n
Jy000n merged commit fa2e464 into develop Jul 7, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat slack-approval-notified Slack 승인 완료 알림 중복 방지용 라벨 🌸 자윤

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 토큰 재발급, 로그아웃, 회원 탈퇴

3 participants