Projects/[Spring] Payment System Project(02FEB26~

Troubleshooting: JWT 액세스 토큰 자동 갱신(Silent Refresh) 실패 이슈

montmer27 2026. 2. 9. 22:51

관련 커밋 링크 : https://github.com/Sungkyunkwan-2/payment-system-project/commit/d6e00322455fa9c04298d72447d7c5b168b6f264

 

fix: resolved auto-logout from silent-refresh · Sungkyunkwan-2/payment-system-project@d6e0032

+ .body(Map.of("success", true, "message", "토큰 재발급 성공"));

github.com

1. 문제 상황 (Issue Description)

  • 현상: 액세스 토큰 만료 시 /api/auth/refresh를 통한 토큰 재발급은 성공(HTTP 200)하나, 직후 재시도되는 원래 API 요청이 계속해서 인증 실패(HTTP 401/403)를 일으키며 최종적으로 강제 로그아웃됨.
  • 영향: 사용자가 로그인 후 일정 시간(토큰 만료 시간)이 지나면 작업 중 세션이 끊김.

2. 원인 분석 (Root Cause Analysis)

이번 이슈의 핵심은 "프런트엔드 저장소의 키(Key) 불일치""토큰 관리 함수의 부재"였다.

  • 발단: SecurityConfigJwtAuthenticationFilter 적용으로 보안 강화.
  • 전개: api-handler.js에 401 에러 대응 로직을 추가했으나, 내부적으로 사용되는 토큰 획득 함수(getToken)가 정의되지 않음.
  • 위기: AI가 임의로 생성한 getToken 함수가 localStorage에서 accessToken이라는 키를 조회하도록 설정됨.
  • 절정 (결정적 원인): 실제 로그인 시 토큰이 저장되는 키 이름은 authToken이었음.
  • 결과: 재발급받은 새 토큰을 엉뚱한 방(accessToken)에 저장하니, 다음 요청 시 getToken이 여전히 비어있거나 만료된 방(authToken)을 참조하게 되어 인증 실패가 반복됨.

3. 해결 방안 (Resolution)

[Step 1] 백엔드: Refresh 엔드포인트 구현 및 보안 설정

  • SecurityConfig에서 /api/auth/refreshpermitAll()에 추가하여 만료된 토큰 상태에서도 접근 가능하도록 설정.
  • AuthController에서 쿠키의 리프레시 토큰을 검증하고, 새 액세스 토큰을 Authorization 헤더에 담아 반환하도록 구현.

[Step 2] 프런트엔드: 토큰 관리 함수 단일화 및 키 일치

api-handler.js 상단에 실제 사용 중인 저장소 키(authToken)를 명시적으로 사용하는 관리 함수를 정의함.

// 토큰 관리 유틸리티 (Key 이름: authToken)
const TOKEN_KEY = 'authToken';

function getToken() {
    return localStorage.getItem(TOKEN_KEY);
}

function setToken(token) {
    localStorage.setItem(TOKEN_KEY, token);
}

[Step 3] 프런트엔드: 재시도(Retry) 로직 보완

401 응답 시 세션을 끊기 전, 재발급을 시도하고 성공 시 저장소를 업데이트한 후 원래 요청을 다시 호출하도록 수정.

4. 결과 확인 (Verification)

  • 검증: 네트워크 탭 확인 결과, 다음의 시퀀스가 정상 작동함.
  1. GET /api/me401 Unauthorized (액세스 토큰 만료)
  2. POST /api/auth/refresh200 OK (새 토큰 발급 및 authToken 저장소 갱신)
  3. GET /api/me (Retry) → 200 OK (갱신된 토큰으로 자동 재시도 성공)

5. 교훈 및 방지 대책 (Lessons Learned)

  • 명시적 환경 확인: 로컬 저장소(Local Storage)나 쿠키를 사용할 때는 반드시 개발자 도구를 통해 실제 저장되는 Key 이름을 먼저 확인해야 함.
  • 추측 기반 개발 지양: 시스템의 공통 라이브러리(api-handler 등)를 수정할 때는 다른 모듈(login, auth 등)과의 의존성 및 함수 정의 여부를 먼저 파악해야 함.