Refresh Token 저장위치와 갱신방법

김키핑·2026년 9월 6일
post-thumbnail

Refresh Token(리프레시 토큰) 이란, 만료된 Access Token(액세스 토큰)을 새로 발급받기 위해 사용하는 장기 인증용 토큰이다.
보안성강화와 사용자 편의성을 위해, 유효기간이 정해져 있고 이를 갱신해야 사용할 수 있다.


서버

Access Token은 서버측에서 관리할 수 있다.
그치만 오늘의 주제는 프론트엔드 관점이므로, 자세히 살펴보지 않는다.

인메모리 (In-Memory)

가장 단순한 방법. Refresh Token을 그냥 자바스크립트 변수(또는 React state/context)에 담아두는 것이다.

const [refreshToken, setRefreshToken] = useState(null);

장점

  • XSS 공격에 가장 안전: 브라우저 저장소(Storage)나 쿠키에 기록되지 않고 메모리 상에만 존재하므로, 스크립트를 통한 토큰 스크래핑(Direct Extraction)을 방지할 수 있다.
  • 구현이 간단함: 별도의 브라우저 API 설정 없이 앱 내부 상태로 손쉽게 관리 가능하다.

단점

  • 휘발성(Persistence 부족): 페이지를 새로고침(F5)하거나 탭을 닫으면 메모리가 초기화되어 토큰이 사라진다.
  • Refresh Token 본연의 역할 수행 불가: 새로고침 시 토큰이 소멸하므로, 재발급을 통한 로그인 상태 유지라는 목적을 달성할 수 없어 단독 저장은 사실상 불가능하다.

갱신 방법

  • In-Memory 업데이트: API 인터셉터(axios interceptor 등)가 401 응답을 수신하면, 기존 메모리의 Refresh Token을 API 요청 바디({ refreshToken })에 담아 재발급 엔드포인트로 전송한다. 응답으로 전달받은 신규 Refresh Token을 상태 업데이트 함수(setRefreshToken)를 통해 메모리 변수에 재할당한다.

쿠키 (Cookie)

서버가 Set-Cookie 헤더를 통해 브라우저 쿠키에 토큰을 저장하도록 지시하는 방식이다. (가장 권장됨)

장점

  • HttpOnly 설정 시 XSS 방어: JavaScript(document.cookie)를 통한 접근이 차단되어 악성 스크립트로 토큰을 탈취할 수 없다.
  • 자동 전송의 편리함: 설정된 경로/도메인으로 요청을 보낼 때 브라우저가 쿠키를 자동으로 첨부한다.

단점

  • CSRF 공격에 취약: 자동 전송 특성 때문에 타 사이트에서의 요청에 악용될 수 있으므로, 보안 옵션 설정이 필수적이다.
속성방어 대상 및 역할
HttpOnlyXSS 방어: JavaScript 접근 차단
Secure중간자 공격(MITM) 방어: HTTPS 암호화 통신 채널에서만 전송 허용
SameSite=Strict / LaxCSRF 방어: 타 사이트에서의 교차 요청 제어 (Strict: 전면 차단, Lax: GET 등 안전한 탐색 시 일부 허용)

갱신 방법

  • 쿠키 자동 전송 & 서버측 교체: 클라이언트가 재발급 API를 호출할 때 브라우저가 쿠키에 저장된 Refresh Token을 credentials: 'include'(또는 withCredentials: true) 옵션에 따라 자동으로 함께 전송한다. 서버는 검증 후 새로 생성된 Refresh Token을 응답의 Set-Cookie 헤더로 재발급하여 브라우저 내 쿠키를 덮어쓴다. (프론트엔드 코드에서 쿠키 값을 직접 조작하지 않음)

SessionStorage

웹 스토리지 API를 활용하여 브라우저의 단일 탭(세션) 내에만 토큰을 보관하는 방식이다.

장점

  • 격리된 세션 관리: 같은 도메인이더라도 탭 단위로 데이터가 독립되어 있어, 동일 브라우저 내 탭별 개별 로그인이나 단기 세션 데이터 관리에 유용하다.
  • 자동 브라우저 정돈: 탭을 닫는 즉시 저장된 데이터가 자동으로 소멸하여 잔여 데이터가 남지 않는다.

단점

  • XSS 공격에 완전히 노출: localStorage와 동일하게 JavaScript로 접근 가능하므로 sessionStorage.getItem()을 통한 XSS 탈취 위협을 방지하지 못한다.
  • Refresh Token 본연의 역할 수행 저해: 탭을 닫으면 토큰이 즉시 소멸하므로, 브라우저 재시작 후에도 로그인 상태를 유지해 주는 Refresh Token의 목적을 달성하기 어렵다.
  • 탭 간 공유 불가: 새로운 탭을 열면 로그인 상태가 이어지지 않고 다시 인증을 거쳐야 한다.

갱신 방법

  • 세션 내 스토리지 키-값 덮어쓰기: sessionStorage.getItem('refreshToken')으로 토큰을 불러와 재발급 API 요청을 보낸 후, 응답으로 수신한 신규 토큰을 sessionStorage.setItem('refreshToken', newRefreshToken)으로 현재 탭 내 스토리지에 덮어써서 업데이트한다.

LocalStorage

웹 스토리지 API를 활용하여 브라우저 내 영구 저장 공간에 토큰을 보관하는 방식이다.

장점

  • 자동 로그인 구현 용이: 브라우저를 닫거나 컴퓨터를 재부팅해도 데이터가 유지되므로, 지속적인 로그인 상태를 손쉽게 유지할 수 있다.
  • 탭 간 상태 공유: 동일한 도메인이라면 여러 탭/창에서 데이터가 동기화되어 어느 탭에서든 동일한 인증 상태를 유지한다.
  • 구현이 간단함: 백엔드의 별도 쿠키/CORS 설정 없이 JavaScript API(getItem, setItem)로 손쉽게 제어 가능하다.

단점

  • XSS 공격에 완전히 노출: JavaScript로 자유롭게 접근할 수 있어, 악성 스크립트 실행 시 localStorage.getItem('refreshToken')으로 토큰이 즉시 탈취된다.
  • 높은 탈취 위험성: 영구 보관 특성상 유효기간이 긴 Refresh Token이 탈취될 경우 장기간 악용될 위험이 매우 크다.

갱신 방법

  • 스토리지 키-값 덮어쓰기: API 인터셉터가 401 응답 수신 시 localStorage.getItem('refreshToken')으로 기존 토큰을 추출해 재발급 API 요청 바디/헤더로 전송한다. 서버 응답으로 전달받은 신규 토큰을 localStorage.setItem('refreshToken', newRefreshToken) 명령어로 덮어써서 업데이트한다.

sisterdy 결론

크로스 플랫폼이나 모바일 호환성을 고려하지 않아도 되는 웹 프론트엔드에서 JWT 토큰을 이용한 로그인을 구현한다면, 액세스 토큰과 리프레시 토큰은 쿠키에 저장할 것입니다.
JavaScript를 통한 직접적인 접근을 차단하여 악성 스크립트 주입 등 XSS 공격을 통한 토큰 탈취 위험을 최소화할 수 있고, 브라우저에서 쿠키를 자동으로 관리해주기 때문에 별도의 토큰 저장 로직을 구현할 필요가 없기 때문입니다.

클라이언트가 로그인을 요청하면, 서버는 먼저 회원가입 여부와 비밀번호를 검증합니다. 인증에 성공하면 짧은 수명의 Access Token과 긴 수명의 Refresh Token을 동시에 생성해 쿠키로 응답하며, 이때 Refresh Token은 서버의 Redis 등에도 함께 저장해둡니다. 이후 API 요청 시 브라우저가 쿠키를 자동으로 첨부하고, Access Token이 만료되어 서버로부터 401 응답을 받으면, 프론트엔드는 저장되어 있던 Refresh Token 쿠키를 이용해 Access Token을 재발급받습니다. 다만 두 토큰을 모두 HttpOnly 쿠키에 저장할 경우 프론트엔드 JS 코드가 Access Token의 만료 여부를 직접 확인할 수 없기 때문에, 401 응답을 감지해 자동으로 재발급을 요청하는 Axios Interceptor 로직과 함께 서버 측에서 재발급 API(/refresh)를 Access Token 검증 대상에서 제외해 주는 설정이 필요합니다. 또한 로그인 상태를 화면에 표시하기 위해서는, 로그인 성공 시 HttpOnly가 아닌 별도의 상태 확인용 쿠키나 응답 값을 함께 내려주는 방식을 고려할 수 있습니다.

이와 같은 방식으로 JWT 로그인을 구현하면, HttpOnly 쿠키로 XSS를 통한 직접적인 토큰 탈취는 막을 수 있지만, CSRF, 네트워크 스니핑, 기기 탈취 등 다른 경로를 통한 토큰 유출을 완벽히 차단할 수는 없으며, 로그인·재발급 API에 대한 무차별 대입 공격이나 비정상적인 반복 요청도 방어할 수 없다는 한계가 있습니다.

이를 위해 HttpOnly, Secure 속성과 함께 CSRF 방어 및 일반 탐색 호환성을 위한 SameSite=Lax 속성을 지정하고, Access Token의 만료 시간은 짧게 설정해 탈취 시 피해 범위를 줄이겠습니다. Refresh Token은 서버에 별도로 저장해 탈취가 의심되는 경우 즉시 무효화할 수 있도록 하고, 재발급 시마다 새로운 Refresh Token으로 교체하는 로테이션(RTR, Refresh Token Rotation) 방식을 적용해, 이미 사용된 구 토큰으로 재발급이 시도될 경우 탈취로 간주하고 해당 계정의 모든 세션을 즉시 차단하겠습니다. 또한 로그인 및 토큰 재발급 요청에 Rate Limiting을 적용하고, 특정 IP에서 요청이 급증하거나 비정상적인 경로 탐색이 발생하는 경우 이를 탐지해 차단하는 방식으로 보안을 강화하겠습니다.

구분내용
저장쿠키 / HttpOnly
발급절차로그인 시 Access + Refresh 동시 발급 / Access 만료 시 Refresh로 Access만 재발급
한계탈취 위험, 로그인·재발급 API에 대한 무차별 대입(Brute Force) / DoS성 공격
극복 ①SameSite=Lax + Secure로 CSRF 방어 (필요시 Anti-CSRF 토큰 병행)
극복 ②RTR(Refresh Token Rotation)로 탈취 탐지 및 무효화
극복 ③Rate Limiting으로 API 공격(무차별 대입/DoS) 방어
profile
양치기소녀

0개의 댓글