
우리가 사용하는 웹(HTTP)은 기본적으로 Stateless(무상태) 라는 특성을 가진다. 쉽게 말해, 서버는 방금 요청을 보낸 사람이 1초 전에 요청을 보낸 그 사람인지 기억하지 못한다.
그래서 우리는 요청을 보낼 때마다 "나 누구누구야! (로그인 했음)"라는 증명서를 계속 보여줘야 한다. 이 증명서가 바로 Access Token(액세스 토큰) 입니다.
그런데 문제는 이 증명서의 유효기간이다.
보안과 편의성, 이 두 마리 토끼를 잡기 위해 등장한 것이 바로 Refresh Token(리프레시 토큰) 을 활용한 자동 로그인(Silent Refresh) 전략이다.
이 메커니즘은 놀이공원 시스템과 아주 비슷하다.
| JWT 시스템 | 놀이공원 비유 | 특징 |
|---|---|---|
| Access Token | 손목 띠 (자유이용권) | 놀이기구(API)를 탈 때마다 보여준다. - 하루동안만 유효함. |
| Refresh Token | 연간 회원권 교환증 | 지갑(DB/쿠키)에 보관. 놀이기구는 못 타지만, 새 손목 띠를 발급받을 수 있다. |
자동 로그인의 원리는 다음과 같다.
손목 띠(Access Token)의 기간이 다 돼서 직원이 "어제꺼로는 못 들어가요~"라고 막으면, 매표소로 달려가서 회원권 교환증(Refresh Token)을 보여주고 새 손목 띠를 받아오는 것이 자동 로그인의 원리와 같다.
사용자는 이 과정이 0.1초 만에 일어나기 때문에 본인의 손목 띠(자유이용권)이 갱신된 지도 모르고 계속 놀이기구를 즐길 수 있게 된다.
이제 실제 개발 코드 관점에서 이 흐름이 어떻게 돌아가는지 4단계로 살펴보겠다.
사용자가 아이디/비밀번호를 입력하면 서버는 두 가지 토큰을 발급한다.
1. Access Token (수명 1시간): 프론트엔드 메모리나 헤더에 저장. API 요청용.
2. Refresh Token (수명 2주): DB에 저장하고, 프론트엔드에는 보안 쿠키(HttpOnly)로 전달.
사용자가 게시글을 쓰려고 POST /api/posts를 요청한다. 그런데 Access Token이 만료된 상태라면?
여기서 프론트엔드의 Axios Interceptor(요청 가로채기) 가 등장한다. 사용자가 401 에러 화면을 보기 전에 중간에서 요청을 가로챈다.
🕵️ Interceptor: "잠깐! 이건 진짜 에러가 아니라 토큰이 만료된 거야. 내가 해결하고 올게."
Interceptor는 즉시 서버의 /refresh API를 호출한다. 이때 쿠키에 담겨있던 Refresh Token이 서버로 전송된다.
서버는 전달받은 Refresh Token을 검사합니다.
1. DB 조회: "이 토큰이 우리 DB에 저장된 그 토큰이 맞는가?"
2. 만료 체크: "2주 유효기간이 아직 안 지났는가?"
모두 통과하면 서버는 새로운 Access Token을 발급해 준다. (보안 정책에 따라 Refresh Token도 새것으로 교체해 준다 = Rotation)
Interceptor는 새 토큰을 받아서 아까 실패했던 게시글 작성 요청 헤더에 끼워 넣고 재요청(Retry) 한다.
결과적으로 사용자는 아무 일도 없었다는 듯이 "게시글 작성 완료!" 메시지를 보게 된다.
Access Token은 DB를 거치지 않고 그 자체로 검증 가능한(Stateless) 방식을 선호하지만, Refresh Token은 DB에 저장하는 것이 일반적이다.
왜냐하면 "강제 로그아웃(토큰 무효화)" 기능을 위해서이다.
만약 사용자가 "모든 기기에서 로그아웃" 버튼을 눌렀거나, 해킹이 의심되는 상황이라면?
DB에 저장된 Refresh Token을 삭제해 버리면 된다. 그러면 해커가 Access Token(1시간짜리)을 가지고 있어도, 1시간 뒤 재발급을 요청할 때 DB에 토큰이 없으므로 영구적으로 차단된다.
결국 자동 로그인 기능은 "짧은 수명의 Access Token으로 보안을 챙기고, 긴 수명의 Refresh Token으로 사용자 편의성을 챙기는 전략" 이다.
여기에 RTR(Refresh Token Rotation) 기법까지 적용하면, 재발급할 때마다 Refresh Token도 새로 바꿔치기 때문에 탈취당했을 때의 위험도 획기적으로 줄일 수 있다.
개발자는 이 보이지 않는 "토큰 핑퐁" 과정을 정교하게 구현함으로써, 사용자에게 "끊김 없는 경험(Seamless Experience)" 을 선물할 수 있다.