백엔드 면접 용어 사전
인증·인가

Refresh Token

짧은 수명의 액세스 토큰을 재발급받기 위한 장수명 토큰. 저장 위치와 회전 전략이 보안 쟁점이다.

Access Token이 만료됐을 때 새로 발급받기 위한 장수명 토큰.

왜 토큰을 두 개로 나누나

하나만 쓰면 딜레마에 빠진다.

  • 수명을 길게 — → 탈취당하면 오랫동안 악용된다
  • 수명을 짧게 — → 사용자가 자주 로그인해야 한다

두 개로 나누면 둘 다 해결된다.

Access TokenRefresh Token
수명짧게 (5~30분)길게 (2주~1개월)
용도매 API 요청에 첨부재발급 요청에만
노출 빈도높음(모든 요청)낮음(가끔)
저장 위치메모리HttpOnly 쿠키
서버 보관안 함한다 (무효화용)

핵심 논리 — 자주 돌아다니는 것은 탈취 위험이 크니 짧게 만들고, 오래 사는 것은 거의 안 돌아다니게 한다.

흐름

다이어그램 로딩 중…

Rotation — 재발급 시 Refresh도 새로 준다

❌ Refresh는 그대로 두고 Access만 새로 발급

  • 탈취된 Refresh가 2주 내내 유효

✅ Refresh도 매번 새것으로 교체하고 옛것은 폐기

  • 탈취해도 정상 사용자가 한 번 갱신하면 무효화된다

재사용 탐지 — Rotation의 진짜 힘

폐기된 Refresh Token이 다시 사용되면 무엇을 뜻하는가?

정상 사용자가 이미 갱신 → 옛 토큰은 폐기됨 그런데 그 옛 토큰이 들어왔다

  • 누군가 옛 토큰을 갖고 있다 = 탈취되었다는 증거

대응 — 해당 사용자의 모든 토큰 계열(family)을 즉시 무효화하고 재로그인을 요구한다. 공격자와 정상 사용자를 구분할 수 없으므로 둘 다 끊는다.

이것이 OAuth 2.0 보안 모범 사례(BCP)가 권장하는 방식이다.

어디에 저장하나

클라이언트 — HttpOnly + Secure + SameSite 쿠키. Path=/auth/refresh 로 제한하면 일반 API 요청에는 아예 실려 가지 않아 노출 면적이 더 줄어든다.

서버 — 반드시 저장한다(Redis 등).

  • key — : refresh:{userId}:{tokenId}
  • value : { deviceInfo, issuedAt, family }
  • TTL — : 만료 시각

서버가 보관해야 무효화할 수 있다. Refresh Token까지 무상태로 만들면 "강제 로그아웃 불가"라는 JWT의 약점이 그대로 남는다.

동시 갱신 경쟁 — 실무에서 자주 겪는 버그

Access 만료 시점에 여러 API 요청이 동시에 401을 받음
→ 각각 Refresh로 재발급 시도
→ Rotation 때문에 첫 번째만 성공, 나머지는 "폐기된 토큰" 판정
→ 재사용 탐지에 걸려 전부 로그아웃  ✗

대응

  • 클라이언트에서 갱신 요청을 하나로 묶는다(진행 중이면 그 결과를 공유)
  • 서버에서 짧은 유예 시간(수 초)을 둬 직전 토큰도 허용

로그아웃

  • Access Token — : 서버가 모르므로 만료를 기다리거나 블랙리스트 Refresh Token : 서버 저장소에서 삭제 → 재발급 불가

Access의 수명이 짧아야 로그아웃이 실질적으로 동작한다. 15분 이상으로 늘리면 "로그아웃했는데 15분간 API가 된다" 는 상태가 된다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 인증·인가 — 세션·JWT·OAuth 2.1·CSRF