애플리케이션을 설계할 때 인증(Authentication)과 인가(Authorization)는 필수적인 보안 요소입니다.

  • 인증(Authentication) : 사용자가 본인임을 증명하는 과정
  • 인가(Authorization) : 인증된 사용자가 무엇을 할 수 있는지를 결정하는 과정

자물쇠

이 두 개념을 이해하다 보면 자연스럽게 토큰(Token)이라는 용어를 접하게 됩니다. 그리고 최근에는 토큰 기반 인증 방식으로 JWT(JSON Web Token)가 많이 사용되고 있습니다.


1. 토큰(Token)이란?

토큰은 사전적으로 “증표”, “표식”이라는 의미를 가집니다. 인증의 관점에서는 서버가 인증 결과를 표현하기 위해 발급하는 증표로 정의할 수 있습니다.

사용자가 인증에 성공하면 서버가 해당 사용자의 신원을 확인한 것입니다. 인증에 성공한 결과를 다음에도 재사용할 수 있도록 서버가 사용자에게 어떤 형태의 값을 발급해준 것을 토큰이라고 합니다.

즉, 토큰은 “이미 인증이 완료되었다”는 사실을 반복적으로 증명하기 위한 수단으로 사용됩니다.

토큰은 다양한 형태를 가질 수 있습니다.

  • 단순 난수 문자열
  • 세션 ID
  • 구조화된 문자열

중요한 점은 단순 문자열이 아니라 인증에 성공한 결과를 표현한 매개체라는 것입니다.


2. 세션(Session)과 토큰(Token)의 차이

토큰을 제대로 이해하려면 세션과 어떤 차이가 있는지를 알아야합니다. 세션은 서버가 클라이언트의 인증 상태를 직접 보관하는 방식입니다.

보통 세션을 사용하는 인증 절차는 다음과 같습니다.

  1. 로그인 성공
  2. 서버가 세션 생성
  3. 세션 정보를 서버 저장소에 보관
  4. 클라이언트는 세션 ID만 전달
  5. 서버는 세션 저장소를 조회하여 인증 확인

반면 토큰을 사용하는 인증 절차는 다음과 같습니다.

  1. 로그인 성공
  2. 서버가 토큰 발급
  3. 클라이언트가 토큰을 보관
  4. 요청 시마다 토큰 전달
  5. 서버는 토큰 자체를 검증

이 차이는 단순 구현 차이가 아니라, 아키텍처 방향성의 차이입니다.

구분 세션 토큰
인증 상태 관리 서버 클라이언트(토큰 보관)
서버 저장소 필요 필요 없음(일반적으로)
확장성 상대적으로 낮음 높음
무효화 쉬움 상대적으로 어려움
대표 사용 전통적인 웹 REST API

3. 그럼 JWT(JSON Web Token)는 무엇인가?

JWT는 토큰의 한 종류입니다. 정확히는 JSON 기반으로 구성된 Self-contained Token(자체 포함 토큰) 표준(RFC 7519)입니다.

Self-contained라는 의미는 토큰 자체에 인증과 인가 판단에 필요한 정보가 포함되어 있다는 뜻입니다.

즉, 사용자 식별 정보와 만료 시간 등 인증에 필요한 정보를 함께 담을 수 있습니다. 따라서 별도의 세션 저장소를 조회하지 않고도 인증의 유효성을 판단할 수 있습니다.


4. JWT의 구성

JWT는 아래와 같이 3개의 영역으로 구성됩니다.

flowchart LR
    H["Header<br/><small>알고리즘 정보</small>"]
    P["Payload<br/><small>Claim</small>"]
    S["Signature<br/><small>전자서명</small>"]

    H --> P --> S

    classDef jwt fill:#E8F4FD,stroke:#1E88E5,stroke-width:2px;
    class H,P,S jwt;

각 영역은 Base64URL로 인코딩되어 있으며, 점(.)으로 구분됩니다.

아래는 테스트 목적으로 실제 생성했던 JWT입니다.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNzcyMjQyNjc5LCJleHAiOjE3NzIyNDYyNzl9.i7vBINqbjZ2G_e6zGrYp9oTNSt-Pmb-hOaARX3hXg-g
영역 문자열
Header eyJhbGciOiJIUzI1NiJ9
Payload eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNzcyMjQyNjc5LCJleHAiOjE3NzIyNDYyNzl9
Signature i7vBINqbjZ2G_e6zGrYp9oTNSt-Pmb-hOaARX3hXg-g

4.1. Header

Header 영역의 문자열을 디코딩하면 다음과 같습니다.

{
  "alg":"HS256"
}

Header에는 JWT가 어떤 알고리즘으로 서명되었는지(alg)와 토큰의 타입(typ) 등의 메타데이터가 포함될 수 있습니다.


4.2. Payload

Payload 영역의 문자열을 디코딩하면 다음과 같습니다.

{
  "sub":"user",
  "iat":1772242679,
  "exp":1772246279
}

Payload는 JWT에서 가장 중요한 영역으로 사용자 식별 정보와 권한, 만료 시간 등 실제 인증에 필요한 정보를 담습니다. Payload에 포함되는 이러한 정보들을 Claim이라고 합니다.

Claim에는 아래와 같이 다양한 정보가 포함될 수 있습니다.

  1. Registered Claim
    • iss (issuer)
    • sub (subject)
    • exp (expiration)
    • iat (issued at)
    • nbf (not before)
  2. Public Claim
    • role
    • userId
    • tenantId 등
  3. Private Claim
    • 특정 시스템 내부 전용 정보

Claim은 단순 데이터 필드가 아닙니다. 어떤 정보를 Claim에 포함할 것인가는 토큰 크기, 보안 정책, 시스템 결합도에 영향을 주는 설계 요소입니다.


4.3. Signature

Signature 영역의 문자열을 디코딩하면 다음과 같습니다.

 ڛ)Jߏ9_xW

Signature는 토큰 위조를 방지하기 위한 서명 값입니다. 서명은 다음과 같은 방식으로 생성됩니다.

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload), 
  secretKey
)

서명 값 검증으로 서버는 다음의 사실을 확인할 수 있습니다.

  • 토큰이 변조되지 않았는가?
  • 신뢰할 수 있는 서버가 발급한 것인가?

여기서 중요한 점이 있습니다. JWT는 암호화 토큰이 아니라, 서명 기반 무결성 검증 토큰이라는 것입니다.

Base64URL로 인코딩되어 있기 때문에 누구나 디코딩이 가능합니다. 하지만 서명 기반이기 때문에 내용을 마음대로 변조할 수는 없습니다.

만약 Payload를 수정하면 Signature도 함께 변경되어야 합니다. 하지만 Signature는 서버만 알고 있는 Secret Key(또는 개인키)로 생성되므로 공격자는 올바른 Signature를 다시 생성할 수 없습니다.

결국 Payload를 임의로 수정하면 서버의 검증 과정에서 위조된 JWT로 판단됩니다.


5. JWT는 암호화된 토큰일까?

JWT는 Base64URL로 인코딩될 뿐 암호화되지 않습니다. 따라서 누구나 Payload 영역의 문자열을 디코딩하여 내용을 확인할 수 있습니다.

따라서 주민등록번호, 비밀번호, 암호화 키와 같은 민감한 정보는 Payload에 저장하면 안 됩니다.

암호화는 되지 않았지만, Signature를 통해 토큰이 위조되었는지는 검증할 수 있습니다. 즉, JWT는 내용을 숨기는 것이 아니라 토큰이 변조되지 않았음을 보장하는 방식입니다.


6. 마무리

JWT는 현대적인 웹 서비스와 REST API에서 널리 사용되는 인증 방식입니다. 안전하게 활용하려면 단순히 라이브러리를 사용하는 것보다 토큰의 구조와 동작 원리를 이해하는 것이 중요합니다.

특히 Payload는 누구나 확인할 수 있으므로 민감한 정보를 저장해서는 안 되며, Signature를 통해 토큰의 무결성을 검증한다는 점을 기억하면 JWT를 올바르게 활용하는 데 도움이 됩니다.