TL;DR

  • JWT 인증 메커니즘의 핵심 개념과 용어를 한눈에 정리한다.
  • JWT 인증 메커니즘이(가) 등장한 배경과 필요성을 요약한다.
  • JWT 인증 메커니즘의 특징과 적용 포인트를 빠르게 확인한다.

1. 개념

JWT 인증 메커니즘은(는) 핵심 용어와 정의를 정리한 주제로, 개발/운영 맥락에서 무엇을 의미하는지 설명한다.

2. 배경

기존 방식의 한계나 현업의 요구사항을 해결하기 위해 이 개념이 등장했다는 흐름을 이해하는 데 목적이 있다.

3. 이유

도입 이유는 보통 유지보수성, 성능, 안정성, 보안, 협업 효율 같은 실무 문제를 해결하기 위함이다.

4. 특징

  • 핵심 정의와 범위를 명확히 한다.
  • 실무 적용 시 선택 기준과 비교 포인트를 제공한다.
  • 예시 중심으로 빠른 이해를 돕는다.

5. 상세 내용

작성일: 2026-02-24 카테고리: Backend / Security / Authentication 포함 내용: JWT, JSON Web Token, Header, Payload, Signature, HMAC, Access Token, Refresh Token, Session 비교, Stateless, 서명 검증, Base64, Claims, Bearer Token


1. JWT의 구조

┌─────────────────────────────────────────────────────────────┐
│                    JWT = 3개의 파트                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                      │
│  .eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6ImFkbWluIn0              │
│  .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c              │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   Header    │  │   Payload   │  │  Signature  │         │
│  │  (Base64)   │. │  (Base64)   │. │  (서명값)   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                    Header                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  {                                                          │
│    "alg": "HS256",   // 서명 알고리즘                       │
│    "typ": "JWT"      // 토큰 타입                           │
│  }                                                          │
│                                                             │
│  → Base64URL 인코딩 (암호화 아님! 누구나 디코딩 가능)       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                    Payload (Claims)                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  {                                                          │
│    "sub":  "user123",      // Subject (사용자 ID)           │
│    "role": "admin",        // 사용자 역할                   │
│    "iat":  1706857200,     // Issued At (발급 시간)         │
│    "exp":  1706860800      // Expiration (만료 시간)        │
│  }                                                          │
│                                                             │
│  → Base64URL 인코딩 (암호화 아님! 누구나 디코딩 가능)       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                    Signature                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  HMAC-SHA256(                                               │
│    Base64(Header) + "." + Base64(Payload),                  │
│    SECRET_KEY                                               │
│  )                                                          │
│                                                             │
│  → SECRET KEY를 아는 서버만 생성/검증 가능                  │
│  → 무결성 보장: 내용이 바뀌면 서명이 달라짐                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                    핵심 포인트                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Header, Payload = Base64 인코딩만 → 누구나 볼 수 있음     │
│  Signature       = SECRET KEY 필요  → 위조 불가능           │
│                                                             │
│  여권 비유:                                                 │
│  ┌───────────────────────────────────────────────────┐     │
│  │ JWT       = 여권                                  │     │
│  │ Payload   = 여권에 적힌 이름/국적 (누구나 볼 수 있음)│     │
│  │ Signature = 정부 직인 (위조 불가)                 │     │
│  │ SECRET KEY= 원본 도장 (정부만 보유)               │     │
│  └───────────────────────────────────────────────────┘     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. 인증 흐름 상세

┌─────────────────────────────────────────────────────────────┐
│              Step 1: 로그인 요청                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Client                          Server                     │
│    │                               │                        │
│    │  POST /login                  │                        │
│    │  { "id": "user123",           │                        │
│    │    "pw": "password" }         │                        │
│    │ ────────────────────────────► │                        │
│    │                               │                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Step 2: 서버에서 JWT 생성                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. Header 생성 → Base64URL 인코딩                          │
│     { "alg": "HS256", "typ": "JWT" }                        │
│     → eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                  │
│                                                             │
│  2. Payload 생성 → Base64URL 인코딩                         │
│     { "sub": "user123", "role": "admin", "exp": ... }       │
│     → eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6ImFkbWluIn0           │
│                                                             │
│  3. Signature 생성 (SECRET KEY 사용)                        │
│     HMAC-SHA256(header + "." + payload, SECRET_KEY)         │
│     → SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c           │
│                                                             │
│  4. 세 파트를 점(.)으로 연결                                │
│     header.payload.signature                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Step 3: JWT를 클라이언트에 전달                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Server                          Client                     │
│    │                               │                        │
│    │  HTTP 200 OK                  │                        │
│    │  {                            │                        │
│    │    "accessToken":  "eyJ...",  │                        │
│    │    "refreshToken": "eyJ..."   │                        │
│    │  }                            │                        │
│    │ ────────────────────────────► │                        │
│    │                               │                        │
│    │                               │ 저장:                  │
│    │                               │ - 브라우저 메모리      │
│    │                               │ - httpOnly Cookie      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Step 4: 이후 모든 API 요청                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Client                          Server                     │
│    │                               │                        │
│    │  GET /api/profile             │                        │
│    │  Authorization: Bearer eyJ... │                        │
│    │ ────────────────────────────► │                        │
│    │                               │                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Step 5: 서버의 JWT 검증                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. JWT를 점(.)으로 분리                                    │
│     header / payload / signature                            │
│                                                             │
│  2. 같은 SECRET KEY로 서명 재계산                           │
│     expected = HMAC-SHA256(header+"."+payload, SECRET_KEY)  │
│                                                             │
│  3. 서명 비교                                               │
│     expected == received_signature? → 통과 or 거절          │
│                                                             │
│  4. exp, iat 확인 (만료 여부)                               │
│                                                             │
│  5. Payload에서 사용자 정보 추출                            │
│     sub, role 등 → 요청 처리에 활용                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3. 서명 검증의 원리

┌─────────────────────────────────────────────────────────────┐
│              왜 위조가 불가능한가?                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  공격자 시나리오:                                           │
│                                                             │
│  원본 JWT:                                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ payload: { "sub": "user123", "role": "user" }       │   │
│  │ signature: abc123 (서버가 SECRET KEY로 생성)         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  공격자가 Payload 변조 시도:                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ payload: { "sub": "user123", "role": "admin" } ← 변조│   │
│  │ signature: abc123 (그대로 유지)                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  서버 검증:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 서버가 변조된 payload로 서명 재계산                  │   │
│  │ → HMAC(변조된 payload, SECRET_KEY) = xyz789          │   │
│  │ → xyz789 ≠ abc123 → 위조 탐지! 요청 거절            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  결론: SECRET KEY 없이는 올바른 Signature를 만들 수 없음   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4. Session 방식과의 비교

┌─────────────────────────────────────────────────────────────┐
│              Session 방식                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Client          Server          DB / Redis                 │
│    │               │                  │                     │
│    │  로그인 요청  │                  │                     │
│    │ ────────────► │                  │                     │
│    │               │  세션 저장       │                     │
│    │               │ ───────────────► │                     │
│    │  sessionId    │                  │                     │
│    │ ◄──────────── │                  │                     │
│    │               │                  │                     │
│    │  API 요청     │                  │                     │
│    │  Cookie: sid  │                  │                     │
│    │ ────────────► │                  │                     │
│    │               │  세션 조회 (매 요청마다)               │
│    │               │ ───────────────► │                     │
│    │               │ ◄─────────────── │                     │
│    │               │                  │                     │
│  특징: 세션은 서버/DB에 저장, 모든 요청마다 DB 조회 필요   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              JWT 방식                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Client          Server                                     │
│    │               │                                        │
│    │  로그인 요청  │                                        │
│    │ ────────────► │                                        │
│    │  JWT 발급     │  (서버에는 아무것도 저장 안 함)         │
│    │ ◄──────────── │                                        │
│    │               │                                        │
│    │  API 요청     │                                        │
│    │  Bearer JWT   │                                        │
│    │ ────────────► │                                        │
│    │               │  서명만 검증 (DB 조회 없음)            │
│    │  응답         │                                        │
│    │ ◄──────────── │                                        │
│                                                             │
│  특징: 서버에 아무것도 저장 안 함, JWT에 모든 정보 포함     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5. JWT가 Session보다 나은 점

┌─────────────────────────────────────────────────────────────┐
│              장점 1. Stateless                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session:                                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Server Memory: { "sid-abc": {userId: 1, role: ...}}│   │
│  │  → 서버 재시작하면 모든 세션 소멸                    │   │
│  │  → Redis 없으면 다중 서버에서 세션 공유 불가         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  JWT:                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Server Memory: (비어 있음)                          │   │
│  │  → 서버 재시작해도 토큰은 유효                       │   │
│  │  → 메모리/Redis 불필요                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              장점 2. 수평 확장 (Scale-out)                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session 방식의 문제:                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  사용자A ─────► Server1 (세션 있음) ✓               │   │
│  │  사용자A ─────► Server2 (세션 없음) ✗ 로그아웃!     │   │
│  │  → Sticky Session 또는 공유 Redis 필요               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  JWT 방식:                                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  사용자A ─────► Server1 (SECRET KEY로 검증) ✓       │   │
│  │  사용자A ─────► Server2 (SECRET KEY로 검증) ✓       │   │
│  │  → 어느 서버든 SECRET KEY만 있으면 검증 가능         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              장점 3. 마이크로서비스 인증 전파                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session 방식:                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Client → Service A → Service B → Service C         │   │
│  │              ↓              ↓              ↓         │   │
│  │           Redis          Redis          Redis        │   │
│  │  → 모든 서비스가 공유 세션 저장소에 의존             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  JWT 방식:                                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Client → Service A → Service B → Service C         │   │
│  │           Bearer JWT  Bearer JWT  Bearer JWT         │   │
│  │  → 토큰을 그대로 전달, 각 서비스가 독립적으로 검증   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              장점 4. Cross-domain / Mobile 친화적           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session/Cookie 방식의 제약:                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  - 쿠키는 동일 도메인에서만 자동 전송               │   │
│  │  - api.example.com ↔ app.example.com: 주의 필요      │   │
│  │  - 모바일 앱에서 쿠키 처리 복잡                     │   │
│  │  - CORS 설정 까다로움                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  JWT 방식의 자유로움:                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  - Authorization 헤더: 도메인 무관 어디서나 작동     │   │
│  │  - iOS, Android, Web 모두 동일하게 처리             │   │
│  │  - CORS 문제 없음                                   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

6. JWT의 단점

┌─────────────────────────────────────────────────────────────┐
│              단점 1. 즉시 무효화 불가                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session:                                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  "이 세션 삭제" → 즉시 로그아웃 완료               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  JWT:                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  토큰 탈취됐어도... 만료 시간까지는 유효            │   │
│  │  서버는 "무효화"할 방법이 없음                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  완화 방법:                                                 │
│  ├── Blacklist: 무효화할 토큰 ID를 DB에 저장             │
│  ├── 짧은 만료 시간: 15분~1시간                         │
│  └── Refresh Token 패턴: Refresh Token만 DB에서 관리    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              단점 2. 토큰 크기                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Session ID:   수십 바이트 (예: "abc123def456...")          │
│  JWT:          수백~수천 바이트 (Header+Payload+Signature)  │
│                                                             │
│  → 매 API 요청마다 헤더에 포함 → 네트워크 오버헤드          │
│  → Claims이 많아질수록 크기 증가                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              단점 3. Payload 노출                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Base64 디코딩은 누구나 가능:                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  eyJzdWIiOiJ1c2VyMTIzIn0                            │   │
│  │  → base64 decode                                    │   │
│  │  → { "sub": "user123", "role": "admin" }  (노출!)  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  주의사항:                                                  │
│  ├── 절대로 비밀번호를 Payload에 넣지 말 것                 │
│  ├── 민감한 개인정보는 Payload에 최소화                     │
│  └── 암호화가 필요하면 JWE(JSON Web Encryption) 사용        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7. Access Token + Refresh Token 패턴

┌─────────────────────────────────────────────────────────────┐
│              Access Token vs Refresh Token                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Access Token                                               │
│  ├── 수명: 짧음 (15분 ~ 1시간)                              │
│  ├── 용도: API 요청 시 인증 수단                            │
│  └── 특징: 탈취되어도 짧은 시간 내 만료                    │
│                                                             │
│  Refresh Token                                              │
│  ├── 수명: 김 (7일 ~ 30일)                                  │
│  ├── 용도: Access Token 재발급에만 사용                     │
│  └── 특징: DB에 저장 → 즉시 폐기 가능                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              전체 흐름                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 로그인                                                  │
│     Client → Server: POST /login                            │
│     Server → Client: accessToken(15분) + refreshToken(7일)  │
│                                                             │
│  2. API 호출                                                │
│     Client → Server: Authorization: Bearer {accessToken}   │
│                                                             │
│  3. Access Token 만료                                       │
│     Client → Server: POST /token/refresh                    │
│                       { refreshToken: "eyJ..." }            │
│     Server → Client: 새 accessToken 발급                   │
│                                                             │
│  4. Refresh Token 만료                                      │
│     → 다시 로그인                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              보안상 이점                                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Access Token 탈취 시:                                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  → 최대 15분만 유효 → 피해 최소화                   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Refresh Token 탈취 시:                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  → DB에서 해당 Refresh Token 즉시 삭제              │   │
│  │  → 더 이상 Access Token 재발급 불가                  │   │
│  │  → 피해 즉시 차단 가능                              │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8. 서명 알고리즘

┌─────────────────────────────────────────────────────────────┐
│              HS256 - HMAC (대칭 방식)                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                 하나의 SECRET KEY                    │   │
│  │                       │                             │   │
│  │         ┌─────────────┴─────────────┐               │   │
│  │         ▼                           ▼               │   │
│  │    서명(Sign)                  검증(Verify)          │   │
│  │   (Auth Server)              (Auth Server)           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  특징:                                                      │
│  ├── 단순하고 빠름                                          │
│  ├── 서명하는 쪽과 검증하는 쪽이 같은 키를 공유            │
│  └── 모놀리식, 단일 서버 환경에 적합                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              RS256 - RSA (비대칭 방식)                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │   Private Key (비공개)      Public Key (공개)        │   │
│  │         │                         │                 │   │
│  │         ▼                         ▼                 │   │
│  │    서명(Sign)               검증(Verify)            │   │
│  │   (Auth Server만)      (누구든 Public Key로)         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  특징:                                                      │
│  ├── Auth Server: Private Key로 서명                        │
│  ├── 다른 서비스: Public Key로만 검증 (Secret 공유 불필요)  │
│  └── 마이크로서비스, 다중 서비스 환경에 적합                │
│                                                             │
└─────────────────────────────────────────────────────────────┘
항목 HS256 (대칭) RS256 (비대칭)
하나의 Secret Key Private Key + Public Key
서명 Secret Key로 서명 Private Key로 서명
검증 같은 Secret Key로 검증 Public Key로 검증
키 공유 검증하는 쪽도 Secret 필요 Public Key만 배포 (안전)
적합 환경 모놀리스, 단일 서버 마이크로서비스, 다중 서비스

관련 키워드

JWT, JSON Web Token, Access Token, Refresh Token, Bearer Token, HMAC, HS256, RS256, Signature, Claims, Payload, Stateless, Session, Base64, 서명 검증, 인증, 인가, 토큰 기반 인증