JWT란 무엇인가
JWT의 개념, 구조, 동작 방식과 검증·디코딩 차이를 학습한 내용을 정리한 글이다.
백엔드나 웹 개발을 공부하다 보면 인증 방식에서 자주 등장하는 개념이 바로 JWT(JSON Web Token) 이다.
처음에는 그냥 “로그인하면 주는 토큰” 정도로 이해하기 쉬운데, 조금 더 살펴보면 JWT는 단순한 문자열이 아니라 사용자 정보와 인증 상태를 담고, 이를 검증할 수 있게 만든 표준 형식이라는 걸 알 수 있다.
저도 처음 JWT를 접했을 때는 세션이랑 뭐가 다른지, 왜 굳이 토큰을 쓰는지, Header, Payload, Signature 같은 구조가 왜 필요한지 헷갈렸다.
그래서 이번 글에서는 JWT를 공부하면서 이해한 내용을 기준으로 정리해보려고 한다.
1. JWT란 무엇인가
JWT는 JSON Web Token의 약자며,
이름 그대로 JSON 형식의 데이터를 웹에서 주고받기 위한 토큰이라고 볼 수 있다.
조금 더 정확하게 말하면, JWT는 당사자 간 정보를 안전하게 전달하기 위한 표준 형식이다.
여기서 중요한 점은 단순히 정보를 담는 것이 아니라, 이 정보가 중간에 바뀌지 않았는지 확인할 수 있다는 점이다.
JWT는 크게 두 가지 특징을 가진다.
- 정보를 담을 수 있다
- 그 정보가 변조되지 않았는지 검증할 수 있다
보통 로그인에 성공하면 서버가 JWT를 만들어서 클라이언트에게 주고, 이후 클라이언트는 요청을 보낼 때 이 토큰을 보고 “이 사용자가 인증된 사용자인가?”를 판단하게 된다.
처음에는 JWT가 암호화된 안전한 문자열이라고 생각할 수 있지만, 사실 기본적인 JWT는 암호화보다 서명에 초점이 맞춰져 있다.
즉, 내용을 숨기는 목적보다는 내용이 바뀌지 않았다는 것을 보장하는 것이 핵심이다.
그래서 JWT 안에는 민감한 개인정보나 비밀번호 같은 내용을 넣으면 안 된다.
2. JWT는 언제 사용하나
JWT가 가장 많이 사용되는 경우는 인증과 권환 처리이다.
예를 들어 사용자가 로그인에 성공했다고 해보자.
그러면 서버는 “이 사용자는 로그인에 성공했다”는 사실을 바탕으로 JWT를 발급하고, 사용자는 이후 요청을 보낼 때마다 이 토큰을 함께 보낸다.
서버는 토큰을 확인한 뒤 요청을 허용할지 결정한다.
이런 방식은 다음과 같은 상황에서 많이 사용된다.
- 로그인 후 여러 API를 호출하는 경우
- 프론트엔드와 백엔드가 분리된 웹 애플리케이션
- 모바일 앱과 서버가 통신하는 경우
- 여러 서비스에서 로그인 상태를 공유하는 경우
특히 SSO(Single Sign-On) 같은 환경에서는 JWT가 자주 활용된다.
토큰 자체가 비교적 가볍고, 여러 서비스에서 공통적으로 다루기 쉬운 구조이기 때문이다.
또 JWT는 단순히 로그인용으로만 쓰이는 것은 아니다.
어떤 시스템이 다른 시스템에 검증 가능한 정보를 전달해야 할 때도 사용할 수 있다.
예를 들어 “이 요청은 어떤 사용자가 보냈고, 언제 발급된 토큰인지” 같은 정보를 전달할 때 JWT가 유용할 수 있다.
3. JWT의 구조: Header, Payload, Signature
JWT는 점(.)을 기준으로 나뉘는 세 부분으로 이루어져 있다.
1
xxxxx.yyyyy.zzzzz
이 세 부분은 각각 다음을 의미한다.
- Header
- Payload
- Signature
처음에는 이 구조가 낯설게 느껴질 수 있지만, 하나씩 보면 그렇게 어렵지 않다.
1) Header
Header는 토큰의 메타데이터 같은 부분이다.
주로 아래 두 가지 정보가 들어간다.
- 이 토큰이 JWT라는 정보
- 어떤 알고리즘으로 서명했는지에 대한 정보
예를 들면 이런 형태이다.
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
여기서 alg는 서명 알고리즘, typ는 토큰 타입을 의미한다.
즉, Header는 “이 토큰을 어떤 방식으로 처리해야 하는지” 알려주는 역할을 한다.
2) Payload
Payload는 실제 데이터를 담는 부분이다.
여기에는 사용자 정보나 토큰에 필요한 여러 속성이 들어가는데, 이 데이터를 클레임(Claim) 이라고 부른다.
예를 들면 다음과 같다.
1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
여기서 sub는 주체, name은 이름, admin은 관리자 여부처럼 해석할 수 있다.
클레임은 크게 세 종류로 나뉜다.
- 등록된 클레임: 표준에서 자주 쓰는 값들 (
iss,exp,sub,aud등) - 공개 클레임: 필요에 따라 공개적으로 정의해서 쓰는 값들
- 비공개 클레임: 서비스 내부에서 자유롭게 정의해서 쓰는 값들
여기서 기억해야 할 점이 있다.
Payload는 인코딩되어 있을 뿐, 암호화가 되어 있는 것은 아니다.
즉, 누구나 디코딩하면 내용을 볼 수 있어서 여기에 민감한 정보는 넣으면 안 된다.
3) Signature
Signature는 JWT의 핵심이라고 볼 수 있다.
이 부분은 Header와 Payload가 중간에 바뀌지 않았는지 확인하기 위해 존재한다.
서버는 Header와 Payload를 특정 알고리즘과 비밀키로 계산해서 Signature를 만든다.
그리고 나중에 토큰이 들어오면 다시 계산해보고, 기존 Signature와 비교한다. 값이 다르면 “이 토큰은 중간에 변조되었구나”라고 판단할 수 있다.
즉, Signature는 토큰의 무결성을 보장하는 역할을 한다.
정리하면 JWT 구조는 다음처럼 이해할 수 있다.
- Header: 토큰에 대한 설명
- Payload: 실제 데이터
- Signature: 위변조 확인용 값
4. JWT는 어떻게 동작하나
JWT의 동작 흐름은 생각보다 단순하다.
먼저 사용자가 로그인 요청을 보내면,
서버는 아이디와 비밀번호 같은 자격 증명을 확인하고, 인증에 성공하면 JWT를 발급한다.
이후에 사용자는 보호된 리소스에 접근할 때마다 JWT를 함께 보내는데, 보통은 HTTP 요청의 Authorization 헤더에 아래처럼 담아서 보낸다. http request Authorization: Bearer <token>
서버는 요청을 받을 때 이 JWT를 확인하는데, 이 과정에서 다음과 같은 동작들을 수행한다.
- 토큰 구조가 정상적인지
- 서명이 맞는지
- 만료 시간이 지나지 않았는지
- 발급자나 대상 정보가 올바른지
이 모든 검사를 통과하면 사용자는 해당 요청을 수행할 수 있다.
JWT를 공부하다 보면 “상태 비저장 방식”이라는 설명도 자주 보게 된다.
이 말은 서버가 세션 정보를 직접 저장하지 않고도, 토큰만으로 어느 정도 인증을 처리할 수 있다는 뜻이다.
물론 JWT만으로 모든 것을 해결할 수 있는 것은 아니다.
사용자 상태를 다시 확인해야 하거나, 권한이 바뀌었는지 체크해야 하거나, 로그아웃 처리 같은 이슈가 있어서 추가적인 저장소 조회가 필요할 수 도 있다.
5. JWT를 사용하는 이유
JWT가 많이 사용되는 이유는 몇 가지로 정리할 수 있다.
1) 구조가 간단하고 가볍다
- JWT는 JSON 기반이라 XML 기반 형식보다 훨씬 간단하고 다루기 쉽기에 웹 환경이나 HTTP 헤더에 담기도 적합하다.
2) 다양한 환경에서 사용하기 쉽다
- 웹, 모바일, API 서버, SPA 등 여러 환경에서 공통적으로 쓸 수 있어 활용 범위가 넓다.
3) 서명 검증이 가능하다
- JWT는 단순히 사용자 식별 문자열 하나를 던지는 방식이 아니라, 토큰이 중간에 바뀌지 않았는지 확인할 수 있기에 인증 구조에서 큰 장점이 된다.
4) 분산 환경에 적합하다
- 서비스가 여러 개로 나뉘어 있거나, 외부 인증 서버와 연동해야 하는 경우에도 JWT는 비교적 잘 어울린다.
정리
- JWT는 가볍고, 표준화되어 있고, 검증 가능하며, 다양한 플랫폼에서 쓰기 쉬운 토큰 방식이라고 볼 수 있다.
6. JWT 사용 시 주의사항
JWT가 편리하지만, 잘못 이해하고 쓰면 보안상 문제가 생길 수 있다.
1) 민감한 정보는 넣으면 안 된다
- JWT는 디코딩이 가능하기 때문에 Payload 안에 들어 있는 내용은 누구나 볼 수 있기에 비밀번호나 주민등록번호 같은 민감한 값은 절대 넣으면 안 된다.
2) 토큰을 너무 오래 유지하지 말기
- JWT는 사실상 자격 증명이기에 한 번 탈취되면 다른 사람이 사용자 행세를 할 수 있다. 이런 위험을 줄이기 위해 만료 시간(
exp)을 적절히 설정해야 한다.
3) 저장 위치를 신중하게 정하기
- 브라우저 저장소에 토큰을 저장하는 경우 보안 이슈가 생길 수 있다.
- 특히 XSS 같은 공격 상황에서는 토큰이 노출될 위험이 있기 때문에 저장 방식은 신중하게 선택해야한다.
4) 토큰 크기를 너무 키우면 안 된다
- JWT 안에 너무 많은 정보를 넣으면 토큰이 커지고, HTTP 헤더 크기 제한 문제나 성능 문제로 이어질 수 있기에 꼭 필요한 정보만 넣는 것이 좋다.
5) 디코딩만 하고 믿으면 안 된다
- JWT를 디코딩해서 내용을 읽을 수 있다고 해서 그 토큰이 진짜라는 뜻은 아니다.
- 반드시 Signature 검증까지 해야 신뢰할 수 있다.
7. JWT 검증, 유효성 검사, 인코딩·디코딩 차이
이름이 비슷해서 헷갈릴 수 있지만 서로 다른 개념들이다.
1) 유효성 검사와 검증의 차이
- 먼저 유효성 검사는 토큰이 형식적으로 저장하는지 확인하는 과정이다.
- 예시:
- 점(
.)으로 세 부분이 나뉘는지 - 각 부분이 올바른 형식인지
- 만료 시간이나 발급 시간 값이 정상적인지
- 점(
반면 검증은 토큰이 진짜인지 확인하는 과정이다.
- 예시:
- 서명이 올바른지
- 발급자가 맞는지
- 대상 정보가 맞는지
- 변조되지 않았는지
즉, 유효성 검사는 “형식이 맞는가”, 검증은 “믿을 수 있는가”에 가깝습니다.
2) 인코딩과 디코딩의 차이
- 인코딩은 JWT를 만드는 과정이다.
Header와 Payload를 JSON으로 만들고, 이를 Base64Url로 바꾼 뒤 Signature를 붙여 최종 토큰 문자열을 만든다. - 반대로 디코딩은 JWT 문자열을 다시 읽을 수 있게 바꾸는 과정이다.
즉, Header와 Payload를 확인하는 작업이다.
여기서 중요한 점은 디코딩은 누구나 할 수 있다는 것이다.
그래서 JWT를 디코딩할 수 있다는 사실 자체는 이상한 일이 아니다.
다만 디코딩이 가능하다고 해서 토큰이 신뢰된다는 뜻은 아니기 떄문에 신뢰 여부에 대해서는 별도의 검증 과정이 필요하다.
8. 세션과 JWT의 차이
JWT를 이해할 때 가장 많이 비교되는 대상이 세션이다.
세션 방식은 서버가 로그인 상태를 직접 저장하고 관리하는 방식이다.
예를 들어 서버는 세션 저장소에 사용자 로그인 상태를 보관하고, 클라이언트는 세션 ID만 전달한다.
반면 JWT 방식은 인증 정보를 토큰에 담아 클라이언트가 전달하고, 서버는 그 토큰을 검증하는 방식에 가깝다.
단순하게 본다면 다음처럼 정리할 수 있다.
- 세션: 서버 저장소 중심
- JWT: 토큰 검증 중심
다만 실제 서비스에서는 분리되어 쓰이기보다 필요에 따라 함께 쓰이기도 한다.
예를 들자면 access token은 JWT로 처리하면서도, refresh token 관리나 로그아웃 처리, 블랙리스트 관리 같은 부분은 Redis나 DB를 함께 사용하는 경우가 많다.
그래서 “JWT를 쓰면 서버 저장이 완전히 필요 없다”라고 이해하기보다는,
인증 구조를 어떻게 설계하느냐에 따라 저장소와 함께 쓰일 수 있다고 보는 것이 맞다고 생각한다.
결론
JWT는 처음 보면 복잡해 보이지만, 구조와 역할을 나눠서 보면 이해할 수 있는 개념이다.
정리해보면 JWT는 다음과 같다.
- JSON 기반의 토큰 표준이다
- Header, Payload, Signature로 구성된다
- 로그인 이후 인증과 권한 처리에 사용된다
- 서명을 통해 위변조 여부를 확인할 수 있다
- 하지만 내용을 숨겨주는 것은 아니기 때문에 민감한 정보는 담으면 안 된다
처음에는 “그냥 로그인 토큰” 정도로만 생각했는데, JWT는 단순한 문자열이 아닌 정보 정달 방식과 검증 방식이 함께 설계된 표준이라는 점이 중요하다고 느꼈다.
