Toolypet
블로그로 돌아가기
Dev

JWT 완벽 가이드 2026 - 구조, 보안, 모범 사례 총정리

JSON Web Token의 구조와 작동 원리부터 보안 취약점과 모범 사례까지. 실전 코드 예제와 함께 JWT 인증을 마스터합니다.

Toolypet Team

Toolypet Team

Development Team

8 분 읽기

JWT 완벽 가이드 2026

"JWT? 그냥 토큰 아니야?"

맞습니다. 하지만 그 "그냥 토큰"이 제대로 구현되지 않으면 심각한 보안 문제가 됩니다. 2026년 기준, 48%의 AI 생성 인증 코드에 보안 취약점이 존재한다는 연구 결과도 있습니다.

이 가이드에서는 JWT의 구조부터 실전 보안 모범 사례까지 완벽하게 다룹니다.


JWT란?

JWT(JSON Web Token)는 두 주체 간에 정보를 안전하게 전달하기 위한 컴팩트하고 자체 포함적인 방식입니다.

주요 특징

특징설명
Stateless서버에 세션 저장 불필요
Self-contained필요한 정보가 토큰 내에 포함
CompactURL, HTTP 헤더에 사용 가능
Signed변조 감지 가능

사용 사례

  • API 인증: Bearer 토큰으로 API 요청
  • SSO (Single Sign-On): 여러 서비스 간 인증 공유
  • 정보 교환: 서명된 데이터 전달
  • Authorization: 권한/역할 정보 포함

JWT 구조

JWT는 .(점)으로 구분된 3개의 Base64URL 인코딩된 부분으로 구성됩니다.

xxxxx.yyyyy.zzzzz
  |      |      |
Header  Payload  Signature

1. Header

{
  "alg": "HS256",
  "typ": "JWT"
}
필드설명
alg서명 알고리즘 (HS256, RS256 등)
typ토큰 타입 (JWT)

2. Payload (Claims)

{
  "sub": "user123",
  "name": "홍길동",
  "email": "hong@example.com",
  "role": "admin",
  "iat": 1708502400,
  "exp": 1708588800
}

등록된 클레임 (Registered Claims)

클레임설명예시
iss발급자 (Issuer)"https://auth.example.com"
sub주체 (Subject)"user123"
aud대상자 (Audience)"https://api.example.com"
exp만료 시간 (Expiration)1708588800
nbf활성화 시간 (Not Before)1708502400
iat발급 시간 (Issued At)1708502400
jtiJWT 고유 ID"abc123"

공개/비공개 클레임

{
  "role": "admin",           // 비공개 클레임
  "email": "user@example.com",
  "permissions": ["read", "write"]
}

3. Signature

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

서명 알고리즘

대칭키 (Symmetric)

알고리즘설명사용 사례
HS256HMAC + SHA-256단일 서버, 간단한 구현
HS384HMAC + SHA-384더 강한 보안
HS512HMAC + SHA-512최고 수준 보안
// Node.js - HS256
const jwt = require('jsonwebtoken');

const token = jwt.sign(
  { userId: 123, role: 'admin' },
  'your-256-bit-secret',  // 동일한 시크릿으로 검증
  { algorithm: 'HS256', expiresIn: '1h' }
);

비대칭키 (Asymmetric)

알고리즘설명사용 사례
RS256RSA + SHA-256마이크로서비스, 공개 검증
RS384RSA + SHA-384높은 보안 요구
RS512RSA + SHA-512최고 수준 보안
ES256ECDSA + P-256짧은 키, 모바일
// RS256 - 비대칭
const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');

// 발급 (Private Key)
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });

// 검증 (Public Key)
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });

알고리즘 선택 기준

상황권장 알고리즘
단일 서버HS256
마이크로서비스RS256
공개 검증 필요RS256 / ES256
모바일/IoTES256 (짧은 키)

실전 구현

Node.js (Express)

const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
const SECRET = process.env.JWT_SECRET; // 환경변수에서 로드

// 토큰 발급
app.post('/login', async (req, res) => {
  const { email, password } = req.body;

  // 사용자 검증 (예시)
  const user = await verifyCredentials(email, password);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const token = jwt.sign(
    {
      sub: user.id,
      email: user.email,
      role: user.role,
    },
    SECRET,
    {
      expiresIn: '1h',
      issuer: 'your-app-name',
    }
  );

  res.json({ token });
});

// 미들웨어로 검증
const authMiddleware = (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, SECRET, {
      issuer: 'your-app-name',
      algorithms: ['HS256'],
    });
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

// 보호된 라우트
app.get('/protected', authMiddleware, (req, res) => {
  res.json({ message: `Hello, ${req.user.email}` });
});

Python (FastAPI)

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from datetime import datetime, timedelta
import os

app = FastAPI()
security = HTTPBearer()
SECRET = os.environ.get("JWT_SECRET")

# 토큰 발급
def create_token(user_id: str, role: str) -> str:
    payload = {
        "sub": user_id,
        "role": role,
        "iat": datetime.utcnow(),
        "exp": datetime.utcnow() + timedelta(hours=1),
    }
    return jwt.encode(payload, SECRET, algorithm="HS256")

# 토큰 검증
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
    token = credentials.credentials
    try:
        payload = jwt.decode(token, SECRET, algorithms=["HS256"])
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

# 보호된 엔드포인트
@app.get("/protected")
def protected_route(user: dict = Depends(verify_token)):
    return {"message": f"Hello, {user['sub']}"}

보안 취약점과 대응

1. Algorithm None 공격

// 악의적인 헤더
{
  "alg": "none",
  "typ": "JWT"
}

대응:

// 허용 알고리즘 명시
jwt.verify(token, secret, { algorithms: ['HS256'] }); // ✅
jwt.verify(token, secret); // ❌ 위험

2. 시크릿 키 약함

// ❌ 위험
const SECRET = 'secret123';

// ✅ 안전 (최소 256비트)
const SECRET = crypto.randomBytes(32).toString('hex');

3. 민감 정보 노출

// ❌ 위험: Payload는 Base64 디코딩 가능
{
  "password": "user_password",  // 절대 금지!
  "creditCard": "1234-5678-9012-3456"
}

// ✅ 안전
{
  "sub": "user123",
  "role": "admin"
}

4. 토큰 탈취

대응 전략:

  • 짧은 만료 시간 (15분 ~ 1시간)
  • Refresh Token 사용
  • HTTPS 필수
  • HttpOnly 쿠키 (XSS 방지)

5. JWT 재사용 (Replay Attack)

// jti (JWT ID) 사용
const token = jwt.sign({
  sub: user.id,
  jti: crypto.randomUUID(), // 고유 ID
}, SECRET);

// 서버에서 jti 블랙리스트 관리

Access Token + Refresh Token

왜 필요한가?

토큰수명용도
Access Token15분 ~ 1시간API 인증
Refresh Token7일 ~ 30일Access Token 갱신

구현 예시

// 로그인 시 두 토큰 발급
app.post('/login', async (req, res) => {
  const user = await verifyCredentials(req.body);

  const accessToken = jwt.sign(
    { sub: user.id, type: 'access' },
    ACCESS_SECRET,
    { expiresIn: '15m' }
  );

  const refreshToken = jwt.sign(
    { sub: user.id, type: 'refresh' },
    REFRESH_SECRET,
    { expiresIn: '7d' }
  );

  // Refresh Token은 HttpOnly 쿠키로
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 7 * 24 * 60 * 60 * 1000,
  });

  res.json({ accessToken });
});

// 토큰 갱신
app.post('/refresh', (req, res) => {
  const refreshToken = req.cookies.refreshToken;

  try {
    const decoded = jwt.verify(refreshToken, REFRESH_SECRET);

    const newAccessToken = jwt.sign(
      { sub: decoded.sub, type: 'access' },
      ACCESS_SECRET,
      { expiresIn: '15m' }
    );

    res.json({ accessToken: newAccessToken });
  } catch (err) {
    res.status(401).json({ error: 'Invalid refresh token' });
  }
});

JWT 디버깅

토큰 디코딩

JWT Decoder에서 토큰을 붙여넣으면:

  • Header 확인
  • Payload 확인
  • 만료 시간 확인
  • 서명 검증 (시크릿 입력 시)

CLI로 디코딩

# Header 디코딩
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d

# Payload 디코딩
echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ" | base64 -d

모범 사례 체크리스트

발급

  • 강력한 시크릿 키 (256비트 이상)
  • 알고리즘 명시 (HS256, RS256)
  • 짧은 만료 시간 (15분 ~ 1시간)
  • 필수 클레임 포함 (sub, iat, exp, iss)
  • 민감 정보 미포함

검증

  • 알고리즘 화이트리스트 (algorithms: ['HS256'])
  • 만료 시간 검증
  • 발급자(iss) 검증
  • 대상자(aud) 검증

저장 및 전송

  • HTTPS 필수
  • HttpOnly 쿠키 (XSS 방지)
  • SameSite 속성 (CSRF 방지)
  • localStorage 지양 (XSS 취약)

FAQ

Q1: JWT vs 세션, 언제 무엇을?

A:

  • JWT: Stateless API, 마이크로서비스, 모바일 앱
  • 세션: 전통적 웹앱, 서버 사이드 렌더링, 즉시 로그아웃 필요

Q2: JWT가 탈취되면?

A: Access Token 탈취 시 만료까지 유효합니다. 따라서:

  • 짧은 만료 시간 설정 (15분)
  • Refresh Token Rotation
  • 의심 시 모든 토큰 무효화

Q3: 왜 localStorage에 저장하면 안 되나요?

A: XSS 공격에 취약합니다. JavaScript로 접근 가능하므로 악성 스크립트가 토큰을 탈취할 수 있습니다. HttpOnly 쿠키가 더 안전합니다.

Q4: RS256이 HS256보다 안전한가요?

A: "더 안전"하다기보다 "다른 용도"입니다.

  • HS256: 동일한 시크릿 공유, 단일 서버
  • RS256: 공개키로 검증, 다중 서비스

Q5: 토큰 블랙리스트가 필요한가요?

A: 즉시 로그아웃이 필요하면 필요합니다. 하지만 stateless의 장점이 줄어듭니다. 짧은 만료 시간 + Refresh Token이 대안입니다.


마무리

JWT 보안의 핵심:

  1. 강력한 시크릿: 256비트 이상 랜덤
  2. 알고리즘 검증: 화이트리스트 방식
  3. 짧은 수명: Access 15분, Refresh 7일
  4. 안전한 저장: HttpOnly + Secure 쿠키
  5. 민감 정보 제외: Payload는 누구나 디코딩 가능

관련 도구

도구용도
JWT DecoderJWT 디코딩 및 검증
Base64 EncoderBase64 인코딩/디코딩
Hash Generator해시 생성
JWT인증보안API웹개발토큰

저자 소개

Toolypet Team

Toolypet Team

Development Team

The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.

Web DevelopmentCSS ToolsDeveloper ToolsSEOSecurity