JWT란 무엇인가 — 토큰 기반 인증의 원리와 구조

로그인 한 번으로 여러 페이지를 자유롭게 오갈 수 있는 것은, 서버가 “이 사람 아까 로그인한 그 사람 맞아”라는 사실을 어딘가에 기억하기 때문이다. 그 기억을 서버 메모리가 아니라 토큰 안에 담아 클라이언트에게 넘겨 버리는 방식이 바로 JWT다. 오늘날 수많은 웹·모바일 서비스의 로그인 뒤편에서 조용히 돌아가는 이 기술을, 이 글은 정의와 3부 구조, 서명과 암호화의 차이, 세션과의 비교, 그리고 저장 위치를 둘러싼 논쟁까지 입문자 눈높이에서 정리한다. 앞서 다룬 OAuth 2.0 동작 원리, 패스키(passkey) 로그인 원리, REST API란 무엇인가 글과 함께 읽으면 현대 인증의 그림이 한결 선명해질 것이다.

JWT란 무엇인가

JWT는 JSON Web Token의 약자로, 당사자 간에 정보를 안전하게 주고받기 위한 개방형 표준이다. JWT 공식 사이트는 이를 “정보를 JSON 객체로 안전하게 전송하기 위한, 간결하고 자체 완결적인(self-contained) 방식을 정의한 개방 표준(RFC 7519)”이라 설명한다. 표준 문서인 RFC 7519의 정의는 더 형식적이다. JWT는 “두 당사자 사이에 전달할 클레임(claims)을 표현하는, 간결하고 URL에 안전한(URL-safe) 수단”이다.

두 표현에서 핵심 키워드는 ‘자체 완결적’과 ‘클레임’이다. 클레임(claim) 은 “사용자 ID는 1234이고, 권한은 관리자이며, 만료는 언제”처럼 어떤 대상에 관한 주장·정보 조각을 뜻한다. 그리고 ‘자체 완결적’이란, 이 토큰 하나만 있으면 서버가 별도로 데이터베이스를 조회하지 않고도 필요한 정보를 그 안에서 꺼내 검증할 수 있다는 의미다. 바로 이 성질이 JWT를 확장성 있는 인증 수단으로 만든다.

JWT의 3부 구조 — 헤더·페이로드·서명

JWT의 실체는 점(.)으로 구분된 세 덩어리의 문자열이다. jwt.io의 표현대로 xxxxx.yyyyy.zzzzz 형태이며, 각 부분은 Base64Url로 인코딩되어 있다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6Ikp
pbiIsImFkbWluIjp0cnVlLCJleHAiOjE3MTk4Nzg0MDB9.dQw4w9WgXcQ_signature

세 부분은 각각 헤더, 페이로드, 서명이다.

헤더(Header)

첫 번째 부분은 토큰의 유형과 서명에 사용한 알고리즘을 담는다. jwt.io 기준 대표적인 헤더는 다음과 같다.

{
  "alg": "HS256",
  "typ": "JWT"
}

alg는 서명 알고리즘(예: HMAC SHA-256), typ은 토큰 유형이 JWT임을 나타낸다.

페이로드(Payload)

두 번째 부분은 실제 정보, 곧 클레임을 담는 몸통이다. RFC 7519는 표준으로 미리 정의된 등록 클레임(registered claims) 일곱 가지를 규정한다. 모두 사용은 선택이지만, 상호 운용성을 위해 이름이 예약되어 있다.

클레임 이름 의미
iss Issuer 토큰을 발급한 주체
sub Subject 토큰의 대상(주로 사용자 ID)
aud Audience 토큰이 의도한 수신자
exp Expiration Time 이 시각 이후에는 토큰을 받아들이면 안 됨
nbf Not Before 이 시각 이전에는 토큰이 유효하지 않음
iat Issued At 토큰이 발급된 시각
jti JWT ID 토큰의 고유 식별자

여기에 서비스가 필요로 하는 사용자 정의 클레임(이름, 권한 등)을 자유롭게 덧붙일 수 있다.

{
  "sub": "1234",
  "name": "Jin",
  "admin": true,
  "exp": 1719878400
}

서명(Signature)

세 번째 부분은 앞의 두 부분이 변조되지 않았음을 보장하는 서명이다. 인코딩된 헤더와 페이로드를 정해진 알고리즘과 비밀로 서명해 만든다. jwt.io에 따르면 JWT는 “비밀 키를 사용하는 HMAC 알고리즘, 또는 RSA·ECDSA를 쓰는 공개키/개인키 쌍”으로 서명할 수 있다. 수신 측은 같은 방식으로 서명을 다시 계산해 일치 여부로 위조를 걸러낸다.

서명일 뿐, 암호화가 아니다 — 가장 흔한 오해

JWT를 처음 접할 때 가장 많이 하는 오해가 “페이로드가 암호화되어 있으니 민감 정보를 넣어도 안전하다”는 생각이다. 이것은 틀렸다.

jwt.io는 분명히 경고한다. “서명된 토큰의 경우, 이 정보는 변조로부터 보호될 뿐 누구나 읽을 수 있다.” Auth0 문서 역시 Auth0가 발급하는 JWT는 암호화가 아닌 서명(JWS, JSON Web Signature) 방식이며 내용이 평문으로 저장된다고 명시한다. Base64Url은 암호화가 아니라 단순 인코딩이라, 누구든 토큰 문자열을 디코딩하면 페이로드를 그대로 읽을 수 있다.

즉 서명은 “이 내용이 도중에 바뀌지 않았다”는 무결성을 보장할 뿐, “이 내용을 남이 못 본다”는 기밀성을 보장하지 않는다. 그래서 실무 원칙은 명확하다. 비밀번호·주민번호·카드번호 같은 민감 정보를 JWT 페이로드에 담아서는 안 된다. 내용을 진짜로 숨겨야 한다면 별도로 암호화하는 JWE(JSON Web Encryption)를 써야 한다.

세션 vs 토큰 — 무엇이 다른가

JWT의 매력을 이해하려면 전통적인 세션 기반 인증과 비교해야 한다. 세션 방식에서는 로그인에 성공하면 서버가 세션 정보를 자기 메모리나 데이터베이스에 저장하고, 클라이언트에는 세션 ID만 쿠키로 건넨다. 이후 요청마다 서버는 그 ID로 자기 저장소를 뒤져 “누구인지”를 확인한다.

Okta의 설명은 토큰 방식의 차이를 이렇게 짚는다. 전통적 방식은 로그인할 때마다 “컴퓨터가 거래 기록을 만들고, 그만큼 메모리 부담이 늘어난다.” 반면 토큰 방식에서는 “세션 기록이 서버에서 공간을 차지하지 않는다.” 필요한 정보가 토큰 자체에 들어 있으니, 서버는 상태를 따로 기억할 필요가 없다. 이를 무상태(stateless) 라고 부른다.

구분 세션 기반 토큰(JWT) 기반
상태 저장 위치 서버 (메모리·DB) 클라이언트 (토큰 자체)
서버 확장성 세션 공유 필요 무상태라 수평 확장 용이
검증 방식 저장소 조회 서명 검증만으로 완결
즉시 무효화 쉬움(세션 삭제) 어려움(만료 전까지 유효)
주 사용처 전통적 웹 앱 API·모바일·MSA·SSO

핵심은 우열이 아니라 트레이드오프다. 서버 여러 대로 확장하는 분산 환경, 브라우저가 아닌 모바일 앱이나 서버 간 통신에서는 무상태 토큰이 편하다. 반대로 로그아웃 즉시 접근을 끊어야 하는 민감한 서비스라면 세션의 ‘즉시 무효화’가 더 안전할 수 있다.

JWT는 어떻게 인증에 쓰이나

실제 흐름은 간단하다.

  1. 로그인: 사용자가 아이디·비밀번호로 인증에 성공한다.
  2. 토큰 발급: 서버가 사용자 정보를 클레임으로 담고 서명한 JWT를 발급해 돌려준다.
  3. 요청 시 첨부: 이후 클라이언트는 요청마다 이 토큰을 HTTP 헤더에 실어 보낸다. MDN의 Authorization 헤더 문서가 정의하듯 Authorization: <인증 스킴> <자격 증명> 형식을 따르며, 토큰에는 보통 Bearer 스킴을 쓴다.
GET /api/profile HTTP/1.1
Host: itinsights.or.kr
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0...
  1. 검증: 서버는 토큰의 서명과 만료 시각을 확인하고, 유효하면 페이로드의 클레임을 근거로 요청을 처리한다.

여기서 자주 등장하는 것이 액세스 토큰(access token)리프레시 토큰(refresh token) 의 조합이다. 수명이 짧은 액세스 토큰으로 평소 요청을 처리하다가, 만료되면 수명이 긴 리프레시 토큰으로 새 액세스 토큰을 발급받는 방식이다. Auth0 문서도 인증에는 ID 토큰을, 자원 접근(인가)에는 액세스 토큰을 요청마다 전달한다고 설명한다. 이 지점에서 JWT는 OAuth 2.0의 토큰 발급 흐름과 자연스럽게 맞물린다.

JWT의 한계와 논쟁

균형을 위해 단점도 분명히 짚어야 한다. JWT는 만능이 아니며, 몇 가지 실무 논쟁을 안고 있다.

  • 즉시 무효화가 어렵다: 무상태라는 장점의 이면이다. 한번 발급된 토큰은 만료 시각 전까지 유효해서, 탈취되거나 로그아웃해도 서버가 곧바로 무력화하기 어렵다. 그래서 블랙리스트, 짧은 만료 + 리프레시 토큰, 토큰 버전 관리 같은 보완책이 필요하다.
  • 저장 위치 논쟁: 브라우저에서 토큰을 어디에 둘지는 오래된 논쟁거리다. localStorage는 자바스크립트로 접근이 쉬운 대신 XSS(교차 사이트 스크립팅) 공격에 노출되고, HttpOnly 쿠키는 XSS에는 강하지만 CSRF(교차 사이트 요청 위조) 대응을 따로 해야 한다. 정답은 하나가 아니라 위협 모델에 달렸다.
  • 페이로드 노출: 앞서 강조했듯 서명은 암호화가 아니다. 민감 정보를 담으면 그대로 새어 나간다.
  • 토큰 크기: 클레임이 많아질수록 토큰이 커지고, 매 요청 헤더에 실려 다니므로 네트워크 부담이 늘 수 있다. 세션 ID 한 줄과 대조되는 지점이다.

정리하면, JWT가 주는 무상태·확장성의 대가로 무효화·저장·크기라는 숙제가 따라온다. “이 서비스가 정말 무상태 토큰의 이점을 필요로 하는가?”를 먼저 물어야 하는 이유다.

정리 — JWT를 언제 써야 하나

정리하면, JWT는 헤더·페이로드·서명 세 부분으로 이루어진, 자체 완결적이고 무상태인 토큰 표준이다. 서버가 상태를 기억하지 않아도 서명 검증만으로 신원을 확인할 수 있다는 것이 핵심 강점이고, 그 대가로 즉시 무효화의 어려움과 저장·보안 설계라는 책임이 따라온다.

그렇다면 언제 JWT를 골라야 할까? 서버 여러 대로 확장하는 분산 시스템, 모바일 앱과 서버가 통신하는 API, 여러 서비스가 로그인을 공유하는 SSO·OAuth 2.0 환경이라면 무상태 토큰의 이점이 뚜렷하다. 반대로 단일 서버의 전통적 웹 앱이거나 로그아웃 즉시 접근 차단이 중요한 민감한 서비스라면, 검증된 세션 방식이 더 단순하고 안전한 답일 수 있다. 인증의 미래로 주목받는 패스키 같은 기술과도 함께 살펴보면 선택지가 넓어진다.

여러분이 지금 설계 중인 서비스는 무상태 확장이 생명인가, 아니면 즉각적인 접근 통제가 우선인가? 도구의 유행이 아니라 요구사항의 성격을 기준으로 고르는 것이 핵심이다. 기술 환경과 보안 요구에 따라 최적의 선택은 달라질 수 있으니, 본 정리는 판단의 출발점으로 활용하길 권한다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다