← 블로그 목록💻 개발·기술
JWT 디코더 완벽 가이드 - JSON Web Token 구조 분석과 디버깅 | HMApps
JWT(JSON Web Token)의 구조와 작동 원리를 이해하고, HMApps JWT 디코더로 토큰을 빠르게 분석하는 방법을 알아보세요. Header·Payload·Signature 해석부터 만료 시간 확인, 보안 주의사항까지 개발자 실전 노하우를 총정리합니다.
JWTJSON Web Token인증토큰개발자도구보안API
🔐 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)이 있습니다.
| 클레임 | 이름 | 의미 |
|---|
iss | Issuer | 토큰 발급자 (예: 서버 URL) |
sub | Subject | 토큰 주제 (보통 사용자 ID) |
aud | Audience | 토큰 수신자 (클라이언트 ID) |
exp | Expiration Time | 만료 시간 (Unix 타임스탬프) |
nbf | Not Before | 이 시간 이전에는 유효하지 않음 |
iat | Issued At | 발급 시간 (Unix 타임스탬프) |
jti | JWT 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초 안에 원하는 정보를 확인할 수 있습니다.