로그인 시스템의 핵심: Access Token과 Refresh Token 쉽게 설명하기
웹사이트나 앱에서 로그인할 때 “Access Token”과 “Refresh Token”이라는 단어를 한 번쯤 들어본 적 있을 겁니다.
하지만, 왜 토큰이 두 개나 필요한지, 그리고 세션 로그인과 뭐가 다른지 헷갈리는 분들이 많습니다.
이 글에서는 입문자도 이해할 수 있도록
“세션 vs 토큰”의 차이부터 “Access Token과 Refresh Token의 관계”, 그리고 보안적으로 안전하게 토큰을 다루는 방법까지 단계별로 정리해 보겠습니다.

1. 로그인 인증 방식의 종류 - 세션 vs 토큰
웹 서비스에서 로그인은 단순히 “아이디와 비밀번호를 확인하는 절차”가 아닙니다.
한 번 로그인한 사용자가 페이지를 이동하거나 새 로고침해도 계속 로그인 상태를 유지할 수 있도록 만드는 게 진짜 핵심이죠.
이때 로그인 상태를 유지하는 대표적인 방식이 바로 세션(Session) 기반 인증과 토큰(Token) 기반 인증입니다.
🔷 세션 기반 인증이란?
세션 기반 인증은 서버가 사용자의 로그인 상태를 직접 기억하는 방식입니다.
사용자가 로그인하면 서버는 사용자에게 고유한 세션 ID(Session ID)를 발급하고, 이 값을 브라우저의 쿠키(Cookie)에 저장합니다.
그다음부터 사용자가 API를 호출하거나 페이지를 이동할 때마다 브라우저는 자동으로 이 세션 ID를 함께 전송합니다.
서버는 매 요청마다 이 세션 ID를 확인하여 “아, 이건 로그인된 사용자네!”라고 판단하고 요청을 처리하죠.
즉, 인증 정보가 서버 쪽(Session 저장소)에 존재하며, 사용자는 단순히 그 저장소의 열쇠(세션 ID)를 들고 다니는 구조입니다.
✔️ 예를 들어보면
▸ 사용자가 로그인 → 서버가 “세션 ID: abc123” 발급
▸ 서버 내부의 메모리나 Redis 등에 "abc123 → userId: 42" 저장
▸ 이후 요청 시 쿠키에 abc123이 함께 전송되면, 서버는 세션 테이블을 조회해 사용자 정보를 가져와 인증 완료
✔️ 단점
1. 서버 부하 증가
로그인 사용자가 많을수록, 서버는 그만큼 많은 세션 정보를 유지해야 합니다.
따라서 대규모 서비스에서는 메모리 부담이 커질 수 있습니다.
2. 확장성(Scalability) 한계
여러 서버(A, B, C)가 운영되는 환경에서는, 사용자가 A서버에서 로그인했는데 B서버가 세션 정보를 모르면 인증이 실패할 수 있습니다.
그래서 보통 세션 클러스터링(공유 스토리지)을 추가로 구축해야 합니다.
🔷 토큰 기반 인증이란?
토큰 기반 인증은 세션 방식과 정반대의 접근을 취합니다.
즉, 서버가 로그인 상태를 직접 기억하지 않고, 인증 정보를 클라이언트(사용자 측)가 스스로 들고 다니는 구조입니다.
로그인에 성공하면 서버는 JWT(JSON Web Token) 같은 형태의 토큰(Token)을 만들어 클라이언트에게 전달합니다.
이 토큰에는 사용자 ID, 만료 시간, 발급 시각 등의 정보가 암호화되어 포함되어 있습니다.
그 이후부터는 클라이언트가 요청할 때마다 Authorization 헤더에 Bearer 토큰값을 담아 전송합니다.
서버는 별도의 세션 저장소를 조회하지 않고, 토큰 안에 들어 있는 서명(Signature)을 검증해서 그 사용자가 진짜인지 확인합니다.
✔️ 장점
1. 서버 부담 감소
서버가 세션 정보를 저장하지 않기 때문에, 로그인 사용자 수가 늘어나도 서버 메모리가 늘 필요 없습니다.
2. 확장성과 유연성
여러 서버, 여러 서비스(API 서버, 이미지 서버 등)에서 동일한 토큰만 있으면 인증을 공유할 수 있습니다.
즉, “한 번 로그인으로 여러 시스템 접근(Single Sign-On)”이 가능합니다.
3. 다양한 환경에 호환
웹, 모바일, 데스크톱 앱, IoT 기기 등 어디서든 같은 방식으로 토큰을 전송할 수 있기 때문에, 하나의 인증 시스템으로 모든 클라이언트를 지원할 수 있습니다.
🔷 세션 기반 인증 vs 토큰 기반 인증
| 구분 | 세션 기반 인증 | 토큰 기반 인증 |
| 인증 정보 저장 위치 | 서버 (메모리, Redis 등) | 클라이언트 (JWT 토큰) |
| 서버 부하 | 사용자 수에 비례하여 증가 | 거의 없음 |
| 다중 서버 환경 | 세션 공유 필요 | 토큰만 검증하면 OK |
| 대표 기술 | Cookie + Session | JWT (JSON Web Token) |
| 보안 관리 | 서버에서 세션 만료 제어 | 토큰 만료 시간 기반 제어 |
2. Access Token의 역할과 한계
사용자가 로그인하면 서버는 “이 사용자가 인증된 사용자임”을 증명할 수 있는 Access Token을 발급합니다.
이 토큰은 로그인 이후 모든 요청에 사용되며, 사용자의 신원을 대신 증명하는 일회용 신분증 역할을 합니다
🔷 Access Token이란?
Access Token은 말 그대로 리소스(API)에 접근할 수 있는 권한을 나타내는 토큰입니다.
서버가 로그인 검증을 마친 사용자에게 “너는 인증된 사용자야”라는 의미로 일시적인 허가증을 발급하는 것입니다.
✔️ 동작 과정 요약
1. 사용자가 이메일/비밀번호로 로그인 요청
2. 서버가 인증에 성공하면 Access Token을 생성
3. 클라이언트(웹·앱)는 이 토큰을 저장하고,
이후 모든 API 요청 시 HTTP 헤더의 Authorization 필드에 Bearer <token> 형식으로 포함
4. 서버는 요청을 받을 때마다 토큰을 검증
▸ 토큰의 서명(Signature)이 올바른지
▸ 만료 기간이 지나지 않았는지
▸ 변조되지 않았는지 확인
5. 검증이 통과되면 요청을 처리하고, 사용자의 권한(Role)에 맞는 데이터를 반환
✔️ 쉬운 비유
Access Token은 마치 놀이공원 입장권과 같습니다.
입구에서 티켓을 구매(로그인)하면, 놀이기구를 탈 때마다 티켓을 보여주는 것처럼, API 요청을 할 때마다 서버에 “내가 유효한 사용자야”라는 토큰을 함께 제시하는 겁니다.
이 토큰에는 보통 다음과 같은 정보가 들어 있습니다
{
"sub": "user_12345",
"name": "홍길동",
"iat": 1730000000,
"exp": 1730003600
}
▸ sub: 사용자 식별자 (Subject)
▸ iat: 발급 시각 (issued at)
▸ exp: 만료 시각 (expiration time)
즉, 서버는 별도의 세션 없이도 토큰 안의 정보를 해석해 사용자를 알아볼 수 있습니다.
이 구조 덕분에 서버 부하가 줄고, 확장성(Scalability)이 높아지는 장점이 있습니다.
🔷 Access Token의 한계
Access Token이 가진 가장 큰 특징은 바로 짧은 유효기간(Short Lifetime)입니다.
보통 5분~1시간 내외로 만료되도록 설정하는데, 그 이유는 명확합니다.
1. 탈취(유출) 위험 대비
Access Token이 노출되면 공격자는 그 토큰을 이용해 서버의 API를 호출할 수 있습니다.
즉, 토큰이 곧 신분증이기 때문에, 이를 훔친 사람은 내 계정으로 위장할 수 있죠.
그래서 Access Token은 짧게 살아야 안전합니다.
설령 유출되더라도 몇 분 내에 만료되어 피해를 최소화할 수 있습니다.
2. 유효기간이 짧아 사용자 불편 발생
하지만 짧은 유효기간은 또 다른 문제를 만듭니다.
Access Token이 만료되면 사용자는 다시 로그인을 해야 하죠.
즉, 짧은 수명 → 보안은 강화되지만 편의성은 떨어지는 구조입니다.
✔️ 예시 시나리오
▸ 사용자가 로그인 후 30분 동안 앱을 사용
▸ Access Token의 유효기간이 20분이라면
→ 20분이 지나고 API 요청 시 “401 Unauthorized” 에러 발생
→ 사용자는 다시 로그인해야 함
이건 사용자 경험(UX) 측면에서 매우 불편하죠.
그래서 실제 서비스에서는 Access Token이 만료되더라도 자동으로 새 토큰을 받는 구조를 사용합니다.
그 핵심이 바로 Refresh Token입니다.
3. Refresh Token으로 보안을 강화하는 방법
Access Token은 짧은 유효기간 덕분에 보안에는 강하지만, 그만큼 자주 만료되어 사용자가 재로그인해야 하는 불편함이 생깁니다.
이 문제를 해결하기 위해 등장한 것이 바로 Refresh Token입니다.
이 토큰은 단순한 보조 수단이 아니라, “안전하게 로그인 상태를 유지하게 해주는 핵심 장치”입니다.
🔷 Refresh Token이란?
Refresh Token은 Access Token이 만료되었을 때 새로운 Access Token을 재발급받기 위한 장기용 토큰입니다.
쉽게 말해, Access Token이 “일회용 입장권”이라면, Refresh Token은 “그 입장권을 다시 발급받을 수 있는 장기 회원증”이라고 할 수 있습니다.
✔️ 기본 흐름 요약
1. 사용자가 로그인 성공 → 서버는 Access Token + Refresh Token 두 가지를 발급
2. 클라이언트는 Access Token으로 API 요청 수행
3. Access Token이 만료되면 서버로 Refresh Token을 전송
4. 서버가 Refresh Token의 유효성을 검증하고, 새 Access Token을 발급
5. 사용자는 다시 로그인하지 않아도 서비스를 계속 이용 가능
이 구조를 통해 사용자는 끊김 없는 로그인 경험을 얻고, 서버는 짧은 수명의 Access Token으로 보안성을 유지할 수 있습니다.
✔️ 예시로 이해해 보기
예를 들어 회사 건물 출입증(Access Token)이 하루짜리라면, 매일 새로 발급받아야 하는 불편이 있겠죠.
하지만 Refresh Token은 인사팀(서버)이 보관하는 장기 등록증입니다.
그 등록증이 유효하다면, 매일 새로운 출입증을 쉽게 재발급받을 수 있습니다.
🔷 보안적으로 중요한 이유
Refresh Token은 로그인 상태를 유지할 수 있는 권한 그 자체입니다.
만약 이 토큰이 유출된다면, 공격자는 무한히 Access Token을 재발급받을 수 있는 권한을 얻게 됩니다.
즉, 계정이 완전히 탈취되는 것과 다름없습니다.
✔️ 그래서, 반드시 다음 원칙을 지켜야 합니다.
1. 서버만 알고 있어야 한다
Refresh Token은 서버 DB에 안전하게 저장하고, 클라이언트에는 절대 평문 형태로 노출되지 않아야 합니다.
보통 HttpOnly 쿠키나 암호화된 Secure Storage를 사용합니다.
2. 한 번 사용 후 폐기(One-Time Use)
사용자가 Refresh Token으로 새 Access Token을 요청했다면, 해당 Refresh Token은 즉시 폐기하고 새 Refresh Token을 재발급해야 합니다.
이를 Token Rotation이라고 부르며, 탈취된 토큰이 재사용되는 것을 방지합니다.
3. 유효기간을 길게 설정하되, 무제한은 금물
보통 2주~1개월 정도가 적당하며, 너무 길면 유출 시 피해가 커지고, 너무 짧으면 자주 로그인이 필요해 UX가 떨어집니다.
4. 서버 검증 로직 필수
Refresh Token은 단순 문자열이 아니라, 서버 DB에 저장된 값과 반드시 일치해야 합니다.
이 검증 절차를 거치지 않으면 위조 토큰 공격에 노출됩니다.
🔷 실무에서의 Refresh Token 관리 전략
✔️ 안전한 저장 위치
▸ 클라이언트: HttpOnly 쿠키에 저장 (JavaScript 접근 불가)
▸ 서버: DB 또는 Redis에 저장하여 재사용 여부, 만료 여부 추적
→ “누가, 언제, 어떤 IP에서 사용했는지” 로그를 남기면 추가 보안 가능
✔️ 자동 재발급 시나리오 (Token Rotation)
▸ Access Token 만료 → 401 에러 발생
▸ 클라이언트가 Refresh Token을 서버로 전송
▸ 서버가 Refresh Token 검증 후 새 Access Token + 새 Refresh Token 발급
▸ 이전 Refresh Token은 즉시 폐기
▸ 클라이언트는 새 토큰으로 세션 갱신
이 과정을 자동화하면 사용자는 로그인 만료를 거의 느끼지 않습니다.
보안성과 사용자 편의성을 동시에 달성할 수 있는 방법입니다.
4. 토큰 관리 베스트 프랙티스 - 안전한 저장과 갱신 전략
Access Token과 Refresh Token을 아무리 잘 설계해도, 토큰을 안전하게 저장하고 주기적으로 갱신하는 전략이 없으면 보안은 쉽게 무너집니다.
많은 초보 개발자들이 “토큰은 그냥 localStorage에 넣으면 되지 않나요?”라고 묻지만, 그건 해커에게 문이 활짝 열려 있는 집과 다를 바 없습니다.
이제 토큰을 올바르게 다루는 방법을 단계별로 살펴보겠습니다.
🔷 클라이언트 측 저장 위치
토큰을 저장할 때는 “누가 이 데이터를 볼 수 있는가”를 기준으로 접근해야 합니다.
Access Token과 Refresh Token은 성격이 다르기 때문에 저장 위치도 달라야 합니다.
✔️ Access Token — 임시 저장 (휘발성)
Access Token은 짧은 수명을 가진 인증 토큰이므로, 가능하면 메모리나 임시 저장소(sessionStorage)에 보관하는 것이 좋습니다.
▸ 브라우저 환경: sessionStorage 또는 메모리 변수
▸ 모바일 앱: OS가 제공하는 Secure Storage (예: iOS Keychain, Android Keystore)
이 방식은 페이지를 새로고침하거나 앱을 종료하면 자동으로 삭제되므로, 토큰 탈취 위험을 크게 줄일 수 있습니다.
📌 왜 localStorage는 위험할까?
localStorage는 자바스크립트 코드로 언제든 접근 가능하기 때문에, XSS(크로스 사이트 스크립팅) 공격이 성공하면 토큰이 그대로 유출됩니다.
✔️ Refresh Token — HttpOnly 쿠키에 안전하게 저장
Refresh Token은 Access Token을 새로 발급받기 위한 가장 민감한 장기 토큰입니다.
따라서 절대로 평문으로 저장하면 안 됩니다.
가장 안전한 방법은 HttpOnly + Secure 속성이 설정된 쿠키에 저장하는 것입니다.
Set-Cookie: refreshToken=abcdef12345;
HttpOnly;
Secure;
SameSite=Strict;
▸ HttpOnly → 자바스크립트로 접근 불가 (XSS 방어)
▸ Secure → HTTPS 연결에서만 전송
▸ SameSite → CSRF 공격 방어
이렇게 저장하면, 클라이언트는 토큰의 존재를 직접 다루지 않아도 되고 서버가 자동으로 쿠키를 확인해 새로운 Access Token을 발급할 수 있습니다.
🔷 자동 토큰 갱신 로직 (Silent Refresh / Token Rotation)
Access Token은 짧은 유효기간을 가지므로, 사용 중 만료되었을 때 자동으로 새로운 토큰을 발급받는 로직이 필요합니다.
이 과정을 Silent Refresh 또는 Token Rotation이라고 부릅니다.
즉, 사용자가 “로그인 연장 버튼”을 누르지 않아도 백그라운드에서 토큰이 자연스럽게 갱신되는 흐름입니다.
✔️ 기본 흐름
1. 클라이언트가 API 요청 → 서버에서 “Access Token 만료됨(401 Unauthorized)” 응답
2. 클라이언트는 Refresh Token을 서버로 전송
3. 서버는 Refresh Token을 검증
▸ 유효성(만료 여부, DB 존재 여부) 확인
▸ 변조 또는 재사용 여부 확인
4. 검증 통과 시, 새로운 Access Token 발급 + 기존 Refresh Token 폐기 (또는 갱신)
5. 클라이언트는 새 Access Token으로 요청을 재시도
이때 Refresh Token을 매번 갱신(Token Rotation)하도록 설정하면 탈취 시 재사용 공격을 방지할 수 있습니다.
즉, 한 번 사용한 Refresh Token은 즉시 무효화시키는 방식입니다.
📌 예시:
사용자가 10월 24일에 로그인해 refreshToken_A를 발급받았다면, 다음 Access Token 갱신 시 서버는 refreshToken_B를 새로 주고, A는 DB에서 즉시 삭제합니다.
🔷 로그아웃 처리
보안의 마지막 단계는 로그아웃 시 토큰을 완전히 무효화하는 것입니다.
특히 Refresh Token은 서버 DB에 저장되어 있으므로, 로그아웃 요청이 들어오면 해당 사용자의 Refresh Token을 즉시 삭제해야 합니다.
✔️ 처리 방식
1. 클라이언트 → /logout 요청 전송
2. 서버 → DB에서 해당 사용자의 Refresh Token 삭제
3. 클라이언트 → 쿠키나 메모리에 남아 있는 모든 토큰 제거
이후에는 해당 Refresh Token으로는 절대 새 Access Token을 발급받을 수 없습니다.
즉, “토큰 재사용 공격(Replay Attack)”을 원천 차단할 수 있습니다.
🔷 요약
| 구분 | 권장 저장 위치 | 유효기간 | 보안 포인트 |
| Access Token | 메모리 / sessionStorage | 짧음 (분 단위) | 탈취되더라도 자동 만료 |
| Refresh Token | HttpOnly Secure 쿠키 | 김 (일~주 단위) | 자바스크립트 접근 차단, HTTPS 필수 |
| Silent Refresh | 자동 재발급 로직 | - | 사용자 경험 유지, Token Rotation 필수 |
| 로그아웃 처리 | DB + 클라이언트 모두 삭제 | 즉시 | 재사용 불가능하게 차단 |
✔ 마무리
Access Token과 Refresh Token은 단순히 로그인 기술 용어가 아닙니다.
그 둘은 현대적인 인증 시스템의 핵심 철학, 즉 “보안은 강화하되 사용자 경험은 해치지 말자”를 실현하는 도구입니다.
Access Token은 빠르고 가벼운 인증 수단으로, 서버의 부담을 줄이고 확장성을 높여줍니다.
반면 Refresh Token은 안정적인 장기 인증 수단으로, 사용자가 로그인 상태를 끊김 없이 유지할 수 있도록 돕습니다.
이 두 토큰이 함께 움직일 때, 우리는 “매번 로그인하지 않아도 안전한 서비스”라는 현대 웹의 기본 기대를 충족시킬 수 있습니다.
정리하자면
▸ 세션 기반 인증은 서버 중심의 전통적인 방식
▸ 토큰 기반 인증(JWT)은 클라이언트 중심의 유연한 방식
▸ Access Token은 짧은 생명으로 신속한 인증을 담당
▸ Refresh Token은 장기적으로 인증을 갱신하는 안정 장치
▸ 안전한 저장과 자동 갱신(Silent Refresh)은 실무 필수 전략
※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.
'5. IT기술노트 > 인프라&개발' 카테고리의 다른 글
| SPA vs MPA: 웹의 진화로 보는 싱글 페이지와 멀티 페이지의 차이 (0) | 2025.10.28 |
|---|---|
| PASETO 이해하기: JWT를 대체하는 안전한 토큰 인증 (0) | 2025.10.27 |
| JWT란? 로그인 인증을 가장 쉽게 이해하는 방법 (0) | 2025.10.25 |
| CORS란 무엇인가? - 브라우저 보안의 핵심과 실무 이해 (0) | 2025.10.20 |
| ESLint: 자바스크립트 코드 품질을 손쉽게 지키는 첫걸음 (0) | 2025.10.19 |