← 블로그 목록

JWT 디코더 완벽 가이드 - JSON Web Token 구조 분석과 디버깅 | HMApps

JWT(JSON Web Token)의 구조와 작동 원리를 이해하고, HMApps JWT 디코더로 토큰을 빠르게 분석하는 방법을 알아보세요. Header·Payload·Signature 해석부터 만료 시간 확인, 보안 주의사항까지 개발자 실전 노하우를 총정리합니다.

광고

🔐 JWT란 무엇인가? — 현대 인증의 핵심

웹 개발을 하다 보면 로그인 후 서버에서 이런 긴 문자열을 받은 경험이 있을 겁니다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Iu2Zjeq4uOuPmSIsImlhdCI6MTcxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

이것이 바로 JWT(JSON Web Token)입니다. 점(.) 세 개로 구분된 세 부분으로 이루어진 이 문자열은 단순한 난수가 아니라, 서버와 클라이언트 사이에서 신원 인증과 정보 교환을 안전하게 처리하는 표준 방식입니다.

JWT는 RFC 7519로 표준화되었으며, RESTful API, OAuth 2.0, OpenID Connect 등 현대 인증 시스템의 사실상 표준으로 자리 잡았습니다. Node.js, Spring Boot, Django, Rails, Go 등 대부분의 백엔드 프레임워크에서 기본 지원하거나 공식 라이브러리가 존재합니다.

하지만 개발·디버깅 과정에서 "이 토큰이 왜 만료되었지?", "페이로드에 어떤 데이터가 들어있지?", "알고리즘이 뭐로 설정되어 있지?" 같은 의문이 생길 때마다 코드를 열거나 외부 사이트에 토큰을 붙여넣는 건 번거롭고 보안상 위험합니다. HMApps JWT 디코더는 브라우저 안에서만 동작하므로 토큰이 서버로 전송되지 않아 완전히 안전합니다.

🔬 JWT 구조 완전 해부 — Header · Payload · Signature

JWT는 점(.)으로 구분된 세 파트로 구성되며, 각 파트는 Base64URL 인코딩된 문자열입니다. (Base64URL은 일반 Base64와 달리 URL 안전 문자만 사용하도록 +는 -, /는 _로 대체합니다.)

① Header (헤더)

JWT의 첫 번째 파트는 토큰 유형과 서명 알고리즘을 담고 있습니다.

// Base64URL 디코딩 후:
{
  "alg": "HS256",
  "typ": "JWT"
}
필드의미주요 값
alg서명 알고리즘HS256, HS512, RS256, RS512, ES256, none
typ토큰 유형JWT (고정값)
kid키 ID (선택)여러 키 관리 시 사용

주의: alg: "none"은 서명을 검증하지 않는다는 의미입니다. 취약한 서버 구현에서 이를 악용해 토큰을 위조할 수 있으므로, 서버에서 반드시 허용 알고리즘을 화이트리스트로 관리해야 합니다.

② Payload (페이로드)

두 번째 파트는 실제 데이터가 담긴 클레임(Claim) 집합입니다. 클레임은 엔티티(보통 사용자)와 추가 데이터에 대한 명세입니다.

// Base64URL 디코딩 후:
{
  "sub": "1234567890",
  "name": "홍길동",
  "email": "[email protected]",
  "role": "admin",
  "iat": 1716239022,
  "exp": 1716242622,
  "iss": "https://api.myapp.com",
  "aud": "myapp-client"
}

JWT 표준에는 7개의 등록된 클레임(Registered Claims)이 있습니다.

클레임이름의미
issIssuer토큰 발급자 (예: 서버 URL)
subSubject토큰 주제 (보통 사용자 ID)
audAudience토큰 수신자 (클라이언트 ID)
expExpiration Time만료 시간 (Unix 타임스탬프)
nbfNot Before이 시간 이전에는 유효하지 않음
iatIssued At발급 시간 (Unix 타임스탬프)
jtiJWT ID토큰 고유 식별자 (중복 방지)

중요: 페이로드는 Base64URL로 인코딩되어 있을 뿐, 암호화되지 않습니다. 누구나 디코딩해서 내용을 볼 수 있으므로, 비밀번호·카드번호 등 민감한 정보는 절대 담으면 안 됩니다. 페이로드는 위변조를 방지할 뿐, 기밀성을 제공하지 않습니다.

③ Signature (서명)

세 번째 파트가 JWT의 핵심인 서명입니다. 다음과 같이 생성됩니다.

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

서명은 헤더와 페이로드가 변조되지 않았음을 보장합니다. 서버는 받은 JWT의 서명을 자신이 보유한 비밀키(또는 공개키)로 재계산해서 일치하는지 검증합니다. 서명이 일치하면 "이 토큰은 우리 서버가 발급한 것이며 변조되지 않았다"고 신뢰할 수 있습니다.

  • HS256/HS512: 대칭키 방식 — 발급·검증에 같은 시크릿 사용, 단순하지만 키 공유 필요
  • RS256/RS512: 비대칭키(RSA) — 개인키로 서명, 공개키로 검증, MSA 환경에서 선호
  • ES256/ES512: 비대칭키(ECDSA) — RSA보다 짧은 키로 동등한 보안, 최신 시스템에서 권장

🛠️ HMApps JWT 디코더 사용법

hmapps.kr/jwt-decoder를 열고 JWT 토큰을 입력창에 붙여넣으면 끝입니다. 별도 설치나 회원가입이 필요 없으며, 모든 처리는 브라우저 안에서만 이루어집니다.

STEP 1 — 토큰 입력

JWT 토큰 전체를 입력창에 붙여넣습니다. 점(.) 세 개가 포함된 형태여야 합니다. Bearer 접두어가 있다면 제거하고 순수한 토큰만 입력하세요.

// 이렇게 입력 (Bearer 제거)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

// ❌ 이렇게 입력하면 안 됨
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

STEP 2 — 디코딩 결과 확인

입력하면 즉시 세 영역이 분리되어 표시됩니다.

  • Header: 알고리즘, 토큰 유형 — JSON 형태로 보기 좋게 포맷됨
  • Payload: 클레임 데이터 — exp, iat는 사람이 읽기 쉬운 날짜/시간으로 자동 변환
  • Signature: 원본 서명 문자열 표시

STEP 3 — 만료 시간 즉시 확인

exp 클레임이 있으면 현재 시간과 비교해 토큰이 유효한지, 만료되었는지를 즉시 알 수 있습니다. 개발 중 "401 Unauthorized" 오류가 발생했을 때 가장 먼저 확인해야 할 것이 바로 토큰 만료 여부입니다.

광고

// exp 예시:
"exp": 1716242622
// → 자동 변환: 2026-05-21 03:43:42 (UTC)
// → 현재 시간 기준 만료 여부 표시

💡 JWT 디코더 실전 활용 사례

① API 디버깅 — 401/403 오류 원인 파악

API 호출 시 인증 오류가 발생하면 즉시 토큰을 디코딩해 확인합니다.

  • 401 Unauthorized: 토큰 만료(exp) 또는 서명 불일치 확인
  • 403 Forbidden: 페이로드의 role이나 scope 클레임이 올바른지 확인
  • 422 Unprocessable Entity: 필수 클레임 누락 여부 확인
// 403 발생 시 확인할 페이로드 예시:
{
  "sub": "user_123",
  "role": "viewer",   // ← "admin" 이어야 하는데 "viewer"
  "exp": 1748000000
}
// 원인: 역할 권한 부족 → 서버에서 올바른 역할로 토큰 재발급 필요

② 토큰 발급 검증 — 개발 중 페이로드 내용 확인

로그인 API를 개발할 때, 서버가 올바른 클레임을 담은 토큰을 발급하고 있는지 즉시 확인할 수 있습니다. Postman이나 curl로 받은 토큰을 JWT 디코더에 붙여넣으면 페이로드 내용을 한눈에 볼 수 있습니다.

# curl로 로그인 후 토큰 받기
curl -X POST https://api.myapp.com/auth/login   -H "Content-Type: application/json"   -d '{"email":"[email protected]","password":"password123"}'

# 응답:
{"access_token": "eyJhbGci...", "expires_in": 3600}

# → 받은 access_token을 JWT 디코더에 붙여넣어 페이로드 확인

③ 토큰 수명 설계 검토

서비스 보안 정책을 검토할 때, 실제 발급된 토큰의 iat(발급 시간)와 exp(만료 시간) 차이를 계산해서 토큰 수명이 정책대로 설정되었는지 확인할 수 있습니다.

// exp - iat = 토큰 수명 (초)
// 예: exp=1716242622, iat=1716239022
// 수명 = 1716242622 - 1716239022 = 3600초 = 1시간

// 일반적인 토큰 수명 권장값:
// Access Token:  15분 ~ 1시간 (짧을수록 보안에 유리)
// Refresh Token: 7일 ~ 30일

④ 알고리즘 확인 — 보안 감사

레거시 시스템이나 서드파티 서비스의 JWT를 분석할 때, 헤더의 alg 필드를 확인해 취약한 알고리즘이 사용되고 있지 않은지 점검할 수 있습니다.

알고리즘상태비고
none⛔ 위험서명 없음, 즉시 비활성화 필요
HS256✅ 사용 가능단일 서버, 내부 서비스에 적합
HS512✅ 권장HS256보다 강력한 해시
RS256✅ 권장MSA, 외부 공개 API에 적합
ES256✅ 최신 권장짧은 키, 높은 보안

⑤ 프론트엔드 개발 — 로컬스토리지 토큰 분석

브라우저 개발자 도구에서 로컬스토리지에 저장된 JWT를 꺼내 디코더에 붙여넣으면 현재 로그인된 사용자 정보와 토큰 상태를 즉시 확인할 수 있습니다.

// 브라우저 콘솔에서:
const token = localStorage.getItem('access_token')
console.log(token)
// → 복사한 토큰을 JWT 디코더에 붙여넣기

⚠️ JWT 사용 시 보안 주의사항

1. 민감한 정보는 절대 페이로드에 담지 말 것

페이로드는 누구나 Base64URL 디코딩으로 볼 수 있습니다. 비밀번호, 카드번호, 주민등록번호, 개인 식별 정보(PII) 등은 절대 JWT 페이로드에 포함하면 안 됩니다. 사용자 ID, 역할, 권한 범위(scope) 정도만 포함하는 것이 바람직합니다.

// ❌ 잘못된 예 — 절대 이렇게 하지 말 것
{
  "sub": "user_123",
  "password": "mysecretpassword",   // 위험!
  "credit_card": "1234-5678-9012-3456"  // 위험!
}

// ✅ 올바른 예
{
  "sub": "user_123",
  "role": "admin",
  "scope": ["read", "write"],
  "exp": 1748000000
}

2. 토큰 저장 위치 선택

프론트엔드에서 JWT를 어디에 저장하느냐는 보안과 편의성의 트레이드오프 문제입니다.

저장 위치XSS 위험CSRF 위험권장 여부
localStorage⛔ 높음✅ 낮음민감도 낮은 서비스에서만
sessionStorage⛔ 높음✅ 낮음세션 단위 서비스에 적합
HttpOnly 쿠키✅ 낮음⚠️ CSRF 토큰으로 보완 필요✅ 보안이 중요한 서비스에서 권장

3. Access Token과 Refresh Token 패턴

보안과 사용자 경험을 모두 잡기 위해 다음 패턴을 사용합니다.

  • Access Token: 짧은 수명(15분~1시간), API 호출에 사용, 메모리나 로컬스토리지에 저장
  • Refresh Token: 긴 수명(7~30일), Access Token 갱신에만 사용, HttpOnly 쿠키에 저장

Access Token이 만료되면 사용자에게 재로그인을 요구하는 대신, Refresh Token으로 조용히 새 Access Token을 발급받아 사용자 경험을 유지합니다. Refresh Token이 탈취되더라도 서버에서 무효화(블랙리스트 등)할 수 있습니다.

4. JWT 무효화의 어려움

JWT의 가장 큰 단점은 중간에 무효화하기 어렵다는 점입니다. 서버가 상태를 저장하지 않는(stateless) 방식으로 동작하기 때문에, 발급된 토큰은 만료 전까지 유효합니다. 이를 해결하기 위한 방법들:

  • 짧은 만료 시간: Access Token을 15~30분으로 설정해 탈취 피해 최소화
  • 블랙리스트: Redis 등에 무효화된 jti를 저장하고 서버에서 매번 확인 (상태 저장 방식으로 복귀)
  • Refresh Token 교체(Rotation): Refresh Token 사용 시 새것으로 교체, 구것은 즉시 무효화
  • 키 교체: 전체 서명 키를 교체하면 모든 기존 토큰이 무효화됨 (비상 시 사용)

🚀 마무리 — JWT 디코더로 더 빠른 개발을

JWT는 현대 웹 개발에서 피해갈 수 없는 기술입니다. 구조를 정확히 이해하고, 적절한 도구를 사용하면 개발·디버깅 속도를 크게 높일 수 있습니다.

HMApps JWT 디코더를 사용하면:

  • ✅ 완전 무료 — 회원가입, 구독 없음
  • ✅ 100% 로컬 처리 — 토큰이 서버로 전송되지 않음
  • ✅ Header · Payload · Signature 자동 분리 표시
  • ✅ Unix 타임스탬프 → 사람이 읽을 수 있는 날짜/시간 자동 변환
  • ✅ 토큰 만료 여부 즉시 확인
  • ✅ JSON 보기 좋게 포맷팅

다음에 API 오류를 디버깅하거나 토큰 페이로드를 확인해야 할 때, hmapps.kr/jwt-decoder를 바로 열어보세요. 30초 안에 원하는 정보를 확인할 수 있습니다.

광고