2025년 10월 29일
세션 기반 인증 vs 토큰 기반 인증 / JWT 학습
인증 상태 유지(Authentication State)
- 인증 상태 유지란 로그인 후 일정 기간 동안 사용자가 다시 로그인하지 않아도 신원이 확인된 상태를 유지하는 것을 의미한다.
- 이를 구현하는 방법은 크게 2가지가 존재한다.
세션 기반 인증 (Session-based)
핵심: 서버가 사용자의 인증 상태를 저장하고 관리 (Stateful)
- 세션 방식은 전통적인 웹사이트에서 많이 사용되며, 서버가 사용자의 로그인 상태를 기억하고 있다.
- 사용자가 아이디/비밀번호로 로그인에 성공하면 서버는 사용자를 식별할 수 있는 고유한 세션 ID를 생성한다.
- 이후 해당 세션 정보를 서버의 메모리나 데이터베이스에 저장한다.
- 그리고 서버는 이 세션 ID를 세션 쿠키에 담아 클라이언트(브라우저)에게 보낸다.
- 클라이언트는 이후 모든 요청에 이 세션 쿠키(세션 ID)를 자동으로 포함하여 서버에 보낸다.
- 서버는 요청에 담긴 세션 ID를 받고, 자신이 저장한 세션 정보와 비교해서 사용자가 로그인 상태임을 확인한다.
- 세션 기반 인증의 장점은 다음과 같다.
- 서버가 모든 인증 상태를 관리하므로, 특정 사용자의 세션을 즉시 무효화시킬 수 있다.
- 중요한 사용자 정보를 서버에만 보관하고 클라이언트에게는 식별자만 보내므로, 민간함 정보가 클라이언트에 노출될 위험이 적다.
- 세션 기반 인증의 단점은 다음과 같다.
- 사용자가 많아질수록 서버가 저장하고 관리해야 할 세션 정보가 늘어나 서버 메모리나 DB에 부하를 준다.
- 서버가 여러 대일 경우, 사용자의 요청이 항상 동일한 서버가 가도록 설정하거나 모든 서버가 세션 정보를 실시간으로 공유해야 하는 복잡함이 있다.
토큰 기반 인증 (Token-based)
핵심: 서버가 인증 정보를 암호화하여 클라이언트에게 맡김 (Stateless)
- 토큰 방식은 주로 JWT(JSON Web Token)를 사용하며, 현대 웹 애플리케이션에서 주로 쓰인다.
- 사용자가 로그인에 성공하면, 서버는 사용자의 정보(ID, 권한 등)와 만료 시간을 담은 토큰(JWT)을 생성하고, 이를 서버만의 비밀 키로 서명한다.
- 서버는 이 서명된 토큰을 클라이언트에게 전송한다.
- 클라이언트는 이 토큰을 받아 로컬 스토리지, 세션 스토리지 또는 쿠키에 저장한다.
- 클라이언트는 이후 모든 요청 시 이 토큰을 HTTP 헤더(주로
Authorization: Bearer <token>)에 포함하여 보낸다. - 서버는 받은 토큰의 서명을 자신이 가진 비밀 키로 검증한다. 서명이 유효하면 토큰에 담긴 정보를 신뢰하고 사용자를 인증한다. (별도 DB 조회가 필요 없음)
- 토큰 기반 인증의 장점은 다음과 같다.
- 서버가 인증 상태를 따로 저장하지 않으므로(Stateless), 서버 부하가 적고 여러 서버 간에 자유롭게 요청을 분산할 수 있다. (MSA에 매우 유리)
- 모바일 앱, 웹, 다른 도메인의 서비스 등 다양한 플랫폼과 환경에서 인증을 공유하기 용이하다.
- 토큰 기반 인증의 단점은 다음과 같다.
- 토큰이 제3자에게 탈취되면 악용될 수 있다. (만료 시간을 짧게 설정해야 함)
- 토큰은 일단 발급되면 만료 전까지 유효하므로, 특정 사용자를 강제로 로그아웃시키기 힘들다. (별도의 블랙리스트 관리 필요)
- 세션 기반과 토큰 기반 인증의 차이를 표로 정리해보면 다음과 같다.
특징 | 세션 기반 (Session-based) | 토큰 기반 (Token-based) |
상태 관리 | Stateful (서버가 상태 저장) | Stateless (서버가 상태 저장 안 함) |
인증 정보 저장 | 서버 (메모리, DB 등) | 클라이언트 (로컬 스토리지, 쿠키 등) |
클라이언트 전송 | 세션 ID (주로 쿠키) | 토큰 (주로 Authorization 헤더) |
확장성 | 낮음 (서버 부하, 세션 공유 필요) | 높음 (서버 부하 적음) |
주요 사용처 | 전통적인 서버 렌더링 웹사이트 | 모바일 앱, API, SPA, 마이크로서비스 |
강제 로그아웃 | 쉬움 (서버에서 세션 삭제) | 어려움 (만료 시점까지 유효) |
JWT (Json Web Token)
- JWT(JSON Web Token)는 웹 환경에서 정보를 JSON 객체 형태로 안전하게 전송하기 위한 개방형 표준(RFC 7519)이다.
- 이전 설명에서 '토큰 기반 인증'의 핵심이 '서버가 인증 상태를 저장하지 않는 것(Stateless)'라고 했었는데, JWT가 바로 이를 가능하게 하는 가장 대표적인 기술이다.
- 조금 더 쉽게 비유하자면, JWT는 '정보가 담긴 위조 불가능한 디지털 자유이용권 팔찌'와 같다.
- 놀이공원 직원이 팔찌를 보고(서명 검증) 이 사람이 놀이공원 이용객인지 바로 알 수 있다.
- 직원은 고객이 누구인지 매번 컴퓨터에 검색해서 확인하지 않는다(Stateless).
- 팔찌에는 "매직패스 고객", "종일권" 같은 정보(Payload)가 이미 적혀있다.
JWT의 3가지 구성 요소
- JWT는
.(점)으로 구분된 세 부분으로 구성된다. aaaaaa.bbbbbb.cccccc(Header) . (Payload) . (Signature)
1️⃣ 헤더 (Header)
- 어떤 종류의 토큰인지, 그리고 어떤 알고리즘으로 서명되었는지를 나타낸다.
- 구성
typ(Type): 토큰의 유형 (보통 "JWT")alg(Algorithm): 서명 생성에 사용된 알고리즘 (예: "HS256" 또는 "RS256")
- 이 JSON 객체를 Base64Url 방식으로 인코딩한 것이 첫 번째 부분(
aaaaaa)이다.
2️⃣ 페이로드 (Payload)
- 토큰을 통해 실제로 전달하려는 정보(데이터)가 담긴 부분이다.
- 이 정보의 한 조각을 클레임(Claim) 이라고 부른다.
- 주요 클레임
iss(Issuer): 토큰 발급자 (예: "https://test.com")sub(Subject): 토큰의 주제 (예: 사용자의 고유 ID)exp(Expiration Time): 토큰 만료 시간iat(Issued At): 토큰 발급 시간- 이 외에도 사용자의 권한, 이름 등 필요한 정보를 자유롭게 추가할 수 있다.
- 이 JSON 객체 역시 Base64Url 방식으로 인코딩된 것이 두 번째 부분(
bbbbbb)이다.
3️⃣ 서명 (Signature)
- 이 토큰이 위조되거나 변조되지 않았음을 증명하는 디지털 서명이다.
- 생성 방식
- 인코딩된 헤더와 인코딩된 페이로드를
.으로 합친다. - 이 값을 서버만 알고 있는 '비밀 키(Secret Key)'를 사용하여 헤더에 명시된 알고리즘(
alg)으로 암호화한다.
- 예시 (HMAC-SHA256)
HMAC-SHA256(secretKey, encodedHeader + "." + encodedPayload)
- 이것이 세 번째 부분(
cccccc)이다.
액세스 토큰과 리프레시 토큰
- JWT는 한 번 발급되면 만료 전까지 유효하다.
- 만약 이 토큰이 탈취당하면, 해커는 토큰 만료 시간까지 사용자의 계정을 마음대로 이용할 수 있다.
- 이 문제를 해결하는 가장 좋은 방법은 액세스 토큰의 만료 시간을 매우 짧게(예: 30분, 1시간) 설정하는 것이다.
- 하지만 이렇게 하면 새로운 문제가 생긴다.
- 사용자가 30분마다 로그인이 풀리고 다시 아이디와 비밀번호를 입력해야 한다. 이건 최악의 사용자 경험(UX)이다.
- 이 딜레마(보안 vs 편의성)을 해결하기 위해 '액세스 토큰'과 '리프레시 토큰'을 도입한다.
액세스 토큰 (Access Token)
- 액세스 토큰은 실제 API 요청 시 사용하는 '단기 출입증' 이다.
- 유효 기간을 30분 정도로 매우 짧게 설정해두기 때문에 해커에게 탈취되어도 30분 뒤면 쓸모가 없어진다.
리프레시 토큰 (Refresh Token)
- 리프레시 토큰은 발급 받은 액세스 토큰이 만료되면 새로운 액세스 토큰으로 교환해주는 '장기 회원권 카드' 이다.
- 유효 기간을 7일, 30일 정도로 매우 길게 설정해둔다는 특징이 있다.
- 이 토큰 자체로는 API에 접근할 수 없고, 오직 새로운 액세스 토큰을 발급받는 용도로만 사용한다.
사용자 로그인 시나리오
- 최초 로그인
- 사용자가 ID/PW로 로그인한다.
- 서버는 인증 성공 시, 액세스 토큰(유효기간 30분)과 리프레시 토큰(유효기간 7일)을 둘 다 발급하여 클라이언트에게 전달한다.
- 서버는 이 리프레시 토큰을 DB에 저장해둔다. (누가 이 토큰을 가졌는지 기록)
- 정상적인 API 요청 (30분 이내)
- 클라이언트는 API 요청 시 액세스 토큰을 헤더에 담아 보낸다.
- 서버는 액세스 토큰이 유효함을 확인하고 데이터를 응답한다.
- 액세스 토큰 만료 (30분 경과)
- 클라이언트가 만료된 액세스 토큰으로 API를 요청한다.
- 서버는 토큰이 만료되었음을 확인하고 401 Unauthorized (인증 만료) 에러를 응답한다.
- 액세스 토큰 재발급 요청
- 클라이언트는
401에러를 받으면, 액세스 토큰이 만료됐다는 사실을 인지하고 숨겨둔 리프레시 토큰을 서버의 토큰 재발급 전용 API(예:/api/refresh)로 보낸다.
- 서버의 리프레시 토큰 검증
- 서버는 받은 리프레시 토큰이 DB에 저장된 토큰과 일치하는지, 아직 유효 기간이 남았는지 확인한다.
- 새로운 액세스 토큰 발급
- 검증이 성공하면, 서버는 새로운 액세스 토큰을 발급하여 클라이언트에게 전달합니다. (보안을 위해 리프레시 토큰도 새로 발급해 주기도 한다.)
- 원래 요청 재시도
- 클라이언트는 새로 발급받은 액세스 토큰으로 (3)번에서 실패했던 API 요청을 다시 시도한다.
- 서버는 유효한 토큰을 확인하고 정상적으로 데이터를 응답한다.
사용자는 중간에 로그인이 끊기는 것을 전혀 느끼지 못하고(편의성), 실제 인증에 사용되는 액세스 토큰은 30분마다 교체된다(보안성).
특징 | 액세스 토큰 (Access Token) | 리프레시 토큰 (Refresh Token) |
목적 | API 요청 시 본인 인증 | 액세스 토큰 재발급 |
유효 기간 | 매우 짧음 (예: 30분 ~ 1시간) | 매우 긺 (예: 7일 ~ 30일) |
전송 빈도 | API 요청 시 매번 전송 | 액세스 토큰 만료 시에만 전송 |
탈취 시 위험 | 낮음 (금방 만료됨) | 높음 (새 액세스 토큰 발급 가능) |
저장 위치 | (상대적) 노출 위험 O | 매우 안전한 곳 (예: HttpOnly 쿠키) |
서버 저장 | 필요 없음 (Stateless) | 필수 (DB에 보관하여 검증/무효화) |
