서버가 세션을 기억하지 않아도 "이 사용자가 누구인지"를 어떻게 알 수 있을까요?

세션 기반 인증에서는 서버가 사용자 정보를 저장해야 합니다. 서버를 수평 확장하면 세션 공유 문제가 발생합니다. JWT(JSON Web Token) 는 인증·인가 정보를 토큰 자체에 담아 서버가 상태를 저장하지 않아도 되는 토큰 기반 인증 방식입니다.

JWT(JSON Web Token)란

JWT 는 사용자 정보와 권한을 JSON 형태로 담고 서명을 붙인 토큰입니다. 서버는 매 요청마다 토큰의 서명만 검증하면 되고, 서명이 유효하면 토큰 안의 정보를 신뢰할 수 있습니다.


JWT 토큰의 구조

JWT 구조

JWT는 헤더(Header) · 페이로드(Payload) · 시그니처(Signature) 세 부분으로 구성됩니다. 각 부분을 Base64Url로 인코딩한 뒤 .으로 이어 붙여 Header.Payload.Signature 형태의 문자열이 됩니다.

헤더(Header)

JWT: 헤더

헤더는 ** 토큰의 메타데이터 **를 담습니다. 토큰 타입(typ = JWT)과 서명 알고리즘(alg = HS256, RS256 등)이 들어가며, 서버는 이 정보를 보고 검증 방식을 결정합니다.

페이로드(Payload)

JWT: 페이로드

페이로드는 ** 클라이언트와 서버가 공유할 데이터(Claim)**를 담는 부분입니다. 사용자 ID, 권한, 발급 시간(iat), 만료 시간(exp) 같은 값이 들어갑니다.

이 부분은 Base64Url ** 인코딩 **만 되어 있어서 누구나 디코딩해서 내용을 확인할 수 있습니다. 비밀번호나 주민등록번호 같은 민감한 정보는 절대 넣으면 안 됩니다.

시그니처(Signature)

jwt-signature-structure.png

시그니처는 인코딩된 헤더와 페이로드에 비밀키(또는 개인키)로 ** 서명한 값 **입니다.

서버는 매 요청마다 시그니처를 다시 계산해서 토큰에 들어 있는 값과 비교합니다. 토큰 내용이 한 글자라도 바뀌면 시그니처가 달라지기 때문에, 위변조를 감지할 수 있습니다.

서명에 사용하는 키는 알고리즘에 따라 다릅니다.

알고리즘서명검증
HS256 (대칭키)Secret Key같은 Secret Key
RS256 (비대칭키)Private KeyPublic Key

HS256은 키 하나로 서명과 검증을 모두 수행하므로 간단하지만, 검증하는 모든 서버가 비밀키를 알아야 합니다. RS256은 Private Key는 발급 서버만 가지고, Public Key만 공개하면 되므로 마이크로서비스 환경에 유리합니다.


JWT 인증 흐름

JWT 발급 및 인증 흐름

  1. 클라이언트가 ID/비밀번호로 로그인 요청을 보낸다.
  2. 서버가 인증에 성공하면 사용자 정보와 권한을 담은 JWT를 발급한다.
  3. 클라이언트는 이후 요청마다 Authorization 헤더에 토큰을 포함시킨다.
PLAINTEXT
curl -X GET "https://example.com/api/v1/resources" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json"
  1. 서버는 토큰의 서명과 만료 시간을 검증한다.
  2. 검증이 통과하면 토큰에 담긴 정보로 요청을 처리하고 응답을 반환한다.

이 과정에서 서버는 세션 저장소를 조회하지 않습니다. 토큰 자체가 인증 정보를 담고 있기 때문입니다.


JWT의 장단점

JWT는 Stateless하기 때문에 수평 확장 시 세션 동기화가 필요 없고, 별도 세션 저장소 없이도 여러 마이크로서비스에서 공통 인증 수단으로 쓸 수 있습니다.

반면 한 번 발급된 토큰이 유출되면 만료 전까지 공격자가 사용할 수 있고, 서버에서 즉시 무효화하기 어렵습니다.

장점설명
Stateless서버가 상태를 저장하지 않아 수평 확장이 쉽다
** 세션 저장소 불필요**Redis 같은 별도 저장소 없이 토큰만으로 인증 가능
** 마이크로서비스 적합**여러 서비스가 같은 JWT를 검증해서 공통 인증 수단으로 사용
단점대응 방법
** 토큰 탈취 시 위험**만료 시간을 짧게 + HTTPS 강제 + Refresh 토큰 조합
** 강제 로그아웃 어려움**짧은 Access Token + Refresh Token + 블랙리스트
** 페이로드 평문 노출**민감정보 제외, 필요시 JWE(암호화된 JWT) 도입

주의할 점

JWT payload는 암호화되지 않는다

JWT의 payload는 Base64 ** 인코딩 **일 뿐 암호화가 아닙니다. 누구나 디코딩해서 내용을 볼 수 있으므로, 비밀번호나 개인정보를 payload에 넣으면 안 됩니다.

토큰이 탈취되면 만료될 때까지 막을 수 없다

서버에 상태를 저장하지 않기 때문에, 토큰이 유출되어도 서버에서 즉시 무효화할 방법이 없습니다. Access Token의 만료 시간을 짧게 설정(15분~30분)하고, Refresh Token으로 갱신하는 구조를 사용해야 합니다. 로그아웃 시 Refresh Token을 블랙리스트에 등록하는 방식으로 보완합니다.

localStorage에 저장하면 XSS에 취약하다

localStorage는 JavaScript에서 자유롭게 접근할 수 있기 때문에, XSS 공격이 성공하면 토큰이 바로 탈취됩니다. httpOnly 쿠키에 저장하면 JavaScript에서 접근할 수 없어 XSS 탈취 위험을 줄일 수 있습니다. 다만 쿠키 기반이면 CSRF 보호가 다시 필요해집니다.


정리

항목설명
JWT사용자 정보 + 권한 + 서명을 담은 Stateless 토큰
구조Header(메타데이터) + Payload(클레임) + Signature(서명)
서명 검증토큰 내용이 바뀌면 서명이 달라져 위변조 감지 가능
HS256 vs RS256대칭키(키 하나) vs 비대칭키(서명/검증 키 분리)
보안 핵심payload 민감정보 금지, 짧은 만료 + Refresh Token, httpOnly 쿠키
댓글 로딩 중...