JWT는 어떻게 생겼나

JWT(JSON Web Token)는 점(.)으로 구분된 세 부분의 문자열입니다. API 인증 헤더에서 Authorization: Bearer eyJhbGci... 형태로 매일 마주치지만, 내부 구조를 알면 디버깅이 훨씬 빨라집니다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9  ← 헤더 (Header)
.eyJzdWIiOiIxMjM0IiwiZXhwIjoxNzUyNDg5NjAwfQ  ← 페이로드 (Payload)
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  ← 서명 (Signature)

앞의 두 부분은 URL-safe Base64로 인코딩된 JSON이라 누구나 디코딩해 읽을 수 있습니다. Base64 인코더에 페이로드 부분만 붙여넣으면 바로 내용이 보입니다. 세 번째 서명 부분만 비밀 키 없이는 만들 수 없는 값입니다.

페이로드의 표준 클레임 읽는 법

클레임의미디버깅에서의 용도
exp만료 시각 (Unix 초)401 오류의 최다 원인 — 현재 시각과 비교
iat발급 시각토큰이 언제 만들어졌는지 확인
nbf유효 시작 시각이 시각 전에는 거부됨 — 서버 시계 차이 문제
iss / aud발급자 / 대상다른 환경(스테이징/운영)의 토큰 혼용 확인
sub주체(사용자 ID)어떤 계정의 토큰인지 확인

exp 값은 Unix 타임스탬프이므로 타임스탬프 변환기에 넣으면 사람이 읽는 시각이 됩니다. "토큰이 왜 거부되지?"의 답은 대부분 이 두 단계(페이로드 디코딩 → exp 변환)로 나옵니다.

서명은 무엇을 보장하나

서명은 "이 토큰을 내가(서버가) 발급했고, 내용이 바뀌지 않았다"는 것을 보장합니다. 헤더와 페이로드를 조금이라도 수정하면 서명 검증이 실패합니다. 알고리즘은 크게 두 계열입니다.

계열대표키 구조적합한 상황
HMAC (대칭)HS256발급·검증이 같은 비밀 키단일 서비스 내부 인증
공개키 (비대칭)RS256, ES256개인 키로 서명, 공개 키로 검증여러 서비스가 검증해야 할 때 (MSA, OAuth)

실무에서 반드시 지켜야 할 것

  • 민감 정보를 페이로드에 넣지 않는다 — 디코딩은 복호화가 아닙니다. 비밀번호, 주민번호, 내부 권한 구조가 토큰에 있으면 그대로 노출됩니다.
  • alg: none을 거부한다 — 헤더의 알고리즘을 none으로 바꿔 서명 검증을 우회하는 고전적 공격이 있습니다. 서버는 허용 알고리즘을 화이트리스트로 고정해야 합니다.
  • 만료 시간을 짧게 유지한다 — 액세스 토큰은 분 단위(15~60분)로 짧게, 갱신은 리프레시 토큰으로 분리하는 것이 표준 패턴입니다.
  • 로그에 토큰 전체를 남기지 않는다 — 로그 유출이 곧 세션 탈취가 됩니다. 남겨야 한다면 앞 8자 정도만.

토큰 디버깅 절차 요약

  1. 점(.) 기준으로 나눠 두 번째 조각을 Base64 인코더로 디코딩
  2. exp타임스탬프 변환기로 변환해 만료 여부 확인
  3. iss·aud가 대상 환경과 일치하는지 확인
  4. 이상이 없다면 서버 측 검증 로직(키, 허용 알고리즘, 시계 오차 허용치)을 점검

모든 디코딩은 브라우저 안에서 처리되므로 운영 토큰을 붙여넣어도 외부로 전송되지 않습니다. Base64 자체의 동작 원리는 Base64 인코딩 가이드에서 다룹니다.

자주 묻는 질문

JWT를 디코딩하면 보안 문제가 되지 않나요?

디코딩 자체는 설계된 동작입니다. JWT의 기밀성은 서명(변조 방지)에 있지 암호화에 있지 않으므로, 애초에 페이로드에 비밀을 넣지 않는 것이 원칙입니다.

토큰이 만료됐는지 서버 없이 확인할 수 있나요?

네. 페이로드의 exp 클레임을 디코딩해 현재 시각과 비교하면 됩니다. 단, 서버가 추가로 토큰을 무효화(로그아웃, 차단)했을 가능성은 서버만 알 수 있습니다.

세션 쿠키 대신 JWT를 쓰는 이유는 무엇인가요?

서버가 세션 저장소 없이 토큰만으로 검증할 수 있어 수평 확장이 쉽기 때문입니다. 대신 발급 후 즉시 무효화가 어렵다는 트레이드오프가 있습니다.