JWT 개념 정리

Jiwon Jung·2025년 11월 24일

스프링(Spring)

목록 보기
15/20

오늘은 팀프로젝트 구현 첫 날이였는데 나는 회원가입, 로그인, 로그아웃 부분을 맡았다.

처음엔 세션으로 구현하다가 JWT로 바꾸게 됐는데 진짜 캠프 시작 이래로 이렇게 머리가 빠개질 뻔한 건 처음이였다.

세션을 어려워 했던 나에게 인증, 인가 부분이 쉬운 내용도 아닌데다가 배우지도 않은 내용을 몇 시간만에 개념도 안 잡힌채로 구현 하려니까 진짜 곤욕이였다.

그래도 부딪히면서 해보니까 배우는 게 많은 거 같아서 좋다.

아직 JPA 연관관계 부분이나 JPQL, 페이징 부분, 비즈니스 로직 구현도 많이 부족한데 빨리 로그인 구현 끝내고 얘네도 같이 공부 해야할 것 같다.

오늘 하루종일 진짜 몸통박치기로만 구현을 해서 개념을 좀 정리 해보려고 한다.


🌱 JWT란?

간단하게 JWT가 뭔지 부터 알아보자.

JWT(JSON Web Token)는 JSON 객체를 사용해서 두 당사자 간에 정보를 안전하고 간결하게 전송하기 위한 개방형 표준(RFC 7519)이다.

JWT는 디지털 서명을 통해 토큰의 무결성과 신뢰성을 보장한다.

🥕 JWT 구성 요소

JWT는 점(.)으로 구분된 세 마디로 구성되어 있다.

1. Header(헤더)

{
  "alg": "HS256",
  "typ": "JWT"
}

토큰의 유형(보통은 JWT)과 서명에 사용된 알고리즘 정보를 담고 있다.

이 JSON 객체는 Base64Url로 인코딩 되어 JWT의 첫 번째 부분을 형성한다.

2. Payload(페이로드)

{
  "sub": "user1",
  "name": "Jane Doe",
  "admin": true
}

사용자 정보나 토큰의 만료 시간 등 실제 전송하려는 데이터를 포함한다.
페이로드에는 클레임(Claims)이 포함되며, 이는 사용자 또는 엔티티에 대한 정보이다.

이 JSON 객체 역시 Base64Url로 인코딩 되어 JWT의 두 번째 부분을 형성한다.

클레임의 종류

  • 등록된 클레임 (Registered Claims)

    • 표준화된 클레임으로, 토큰에 대한 정보를 제공한다.
    • ex. iss (발급자), exp (만료 시간), sub (주제)
  • 공개 클레임 (Public Claims)

    • 사용자 정의 클레임으로, 충돌을 방지하기 위해 URI 형식의 고유한 이름을 사용하는 게 좋다.
  • 비공개 클레임 (Private Claims)

    • 당사자 간에 협의된 클레임이다.
    • ex. userId, roles

3. Signature(서명)

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

헤더와 페이로드를 결합하고, 비밀 키를 사용하여 생성된 서명이다.

서명은 토큰이 변조되지 않았음을 확인하는데 사용한다.

🤔 JWT 전체 구조

xxxxx.yyyyy.xxxxx

✅ 실제 예시

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

🚨 잠깐! 서명이 뭘까?

JWT는 클라이언트와 서버 간에 신뢰할 수 있는 정보 전달을 위해 사용한다고 했다.
하지만 JWT는 암호화 되지 않고, 그렇기에 누구나 페이로드의 내용을 볼 수 있다.

그럼 JWT는 어떻게 토큰의 무결성과 신뢰성을 보장할까? 바로 서명(Signature) 덕분이다!

  • 무결성 보장: 서명을 통해 토큰이 생성된 이후 변경되지 않았음을 확인
  • 인증된 발행자 확인: 서명 검증을 통해 토큰이 신뢰할 수 있는 발행자로부터 왔는지 확인

🌱 대칭 서명 방식과 비대칭 서명 방식

JWT 서명 방식은 대표적으로 HS256(대칭)과 RS256(비대칭)이 있다.

🥕 비대칭 서명 방식

  • 개인 키(Private Key)로 서명, 공개 키(Public Key)로 검증
  • 공개 키를 여러 서버에 안전하게 배포 가능함
  • 계산 비용이 좀 더 크고 구현이 복잡함

나는 대칭을 사용했으므로 대칭에 대해서만 더 자세히 알아본다.

🥕 대칭 서명 방식

대칭 서명 방식은 오직 하나의 비밀 키(Secret Key)를 사용하여 서명을 생성하고 검증한다.
핵심은 서버만이 비밀 키를 알고 있다는 것이다.
클라이언트는 비밀 키를 알지 못하고, 토큰의 서명만을 가진다.

🤔 대칭 서명 방식의 작동 원리

1️⃣ 서명 생성 (토큰 발행 시)

  • 서버는 헤더와 페이로드를 결합한다.
  • 지정된 알고리즘(HMAC SHA256 등)을 사용하여 비밀 키로 서명을 생성한다.
  • 생성된 서명을 헤더와 페이로드에 추가하여 최종 JWT를 만든다.

2️⃣ 서명 검증 (토큰 검증 시)

  • 서버는 클라이언트로부터 받은 JWT를 헤더, 페이로드, 서명으로 분리한다.
  • 같은 비밀 키로 헤더와 페이로드를 사용하여 서명을 다시 생성한다.
  • 새로 생성한 서명과 토큰에 포함되어 있는 서명을 비교한다.
  • 일치하면 토큰이 변조되지 않았다고 판단하고 요청을 처리한다.
  • 일치하지 않으면 토큰이 변조되었다고 판단하고 요청을 거부한다.

🤔 흐름 보기

  1. 서버에서 JWT 생성
  • 헤더
{
  "alg": "HS256",
  "typ": "JWT"
}
  • 페이로드
{
  "sub": "user1",
  "role": "admin"
}
  • 비밀 키: mysecretkey123456789

  • 서명 생성

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "mysecretkey123456789"
)
  • 최종 JWT
header.payload.signature
  1. 클라이언트의 요청
  • 클라이언트는 서버로부터 받은 JWT를 저장(로컬 스토리지 or 쿠키)하고, 이후 요청 시 마다 해당 토큰을 서버에 함께 전송한다.
  1. 서버에서 JWT 검증
  • 서버는 받은 JWT를 분리하여 헤더와 페이로드를 추출한다.
  • 동일한 비밀키 mysecretkey123456789로 서명을 재생성한다.
  • 새로 생성한 서명과 클라이언트 토큰의 서명을 비교하여 일치 여부를 확인한다.

🤔 대칭 서명 방식의 장단점

대칭 서명 방식은 비밀 키 하나만으로 서명 생성과 검증이 가능해 간단하고 연산이 빠르지만, 비밀 키가 노출되면 보안에 큰 위협이 되고 여러 서버나 서비스 간에 비밀 키를 공유해야 하는 경우 보안 위험이 증가한다.

그렇기에 비밀 키는 서버 내 안전한 공간에만 저장하고, 비밀 키에 접근할 수 있는 권한은 최소 인원에게만 부여해야 하며, 주기적으로 변경해야한다.

0개의 댓글