티스토리 뷰
JWT와 현대 웹 인증 아키텍처 - Stateless 인증: JWT와 현대 인증 구조
웹 애플리케이션의 인증 방식은 웹 아키텍처의 변화와 함께 지속적으로 발전해 왔습니다. 초기 웹에서는 HTTP의 Stateless 특성 때문에 사용자 상태를 유지하기 어려웠고, 이를 보완하기 위해 Cookie가 등장했습니다. 이후 보안성과 관리 측면에서 개선된 방식으로 Server Session 기반 인증 구조가 널리 사용되었습니다.
그러나 웹 서비스 규모가 커지고 시스템 구조가 점점 분산되면서 Session 기반 인증 방식 역시 여러 한계를 드러내기 시작했습니다. 특히 API 중심 아키텍처와 MSA 환경에서는 서버가 사용자 상태를 직접 저장하는 방식이 확장성 측면에서 부담이 되는 경우가 많았습니다.
이러한 흐름 속에서 등장한 방식이 Token 기반 인증, 그 중에서도 널리 사용되는 JSON Web Token(JWT) 입니다. 이 글에서는 JWT의 구조와 인증 방식, 그리고 현대 웹 아키텍처에서 Token 기반 인증이 사용되는 이유를 정리해 보려고 합니다.
Token 기반 인증이 등장한 배경
Session 기반 인증은 서버가 사용자 상태를 직접 관리한다는 점에서 Cookie 기반 인증보다 보안적으로 개선된 방식이었습니다. 하지만 이 방식은 구조적으로 서버 상태 저장(Server State)에 의존합니다.
사용자가 로그인하면 서버는 Session을 생성하고, 해당 Session 정보를 메모리나 데이터베이스 등에 저장합니다. 이후 클라이언트는 Cookie를 통해 Session ID를 전달하고 서버는 이를 조회하여 사용자 상태를 확인합니다.
이 구조는 단일 서버 환경에서는 비교적 단순하게 동작합니다. 그러나 시스템 규모가 커지면서 서버가 여러 대로 늘어나는 경우 상황이 조금 복잡해집니다. 여러 서버가 동일한 Session 정보를 공유해야 하기 때문입니다.
이를 해결하기 위해 Sticky Session, Session Replication, Redis 기반 Session Store 같은 다양한 방식이 등장했지만, 이러한 접근 방식은 결국 서버 측에서 사용자 상태를 저장한다는 구조적 전제를 유지합니다.
특히 최근의 웹 서비스 환경에서는 다음과 같은 변화가 나타났습니다.
모바일 애플리케이션 증가
SPA(Single Page Application) 등장
외부 API 호출 증가
마이크로서비스 아키텍처 확산
이러한 환경에서는 인증이 브라우저 중심 구조를 넘어 다양한 클라이언트에서 사용됩니다. 또한 서비스가 여러 개의 독립적인 마이크로서비스로 분리되면서 인증 정보를 서비스 간에 전달해야 하는 상황도 많아졌습니다.
이 과정에서 서버가 상태를 저장하지 않는 Stateless 인증 방식이 점점 더 중요해졌습니다. Token 기반 인증은 이러한 요구를 해결하기 위해 널리 사용되기 시작했습니다.
JSON Web Token이란 무엇인가
JWT는 JSON Web Token의 약자로, JSON 형식의 데이터를 기반으로 한 토큰 표준입니다. 이 표준은 IETF에서 정의한 RFC 7519 문서에서 규정되어 있습니다.
JWT는 인증 정보를 하나의 토큰 안에 담아 클라이언트와 서버 사이에서 전달할 수 있도록 설계되었습니다. 토큰은 서명을 통해 무결성을 검증할 수 있기 때문에 서버는 별도의 세션 저장소 없이도 토큰의 유효성을 확인할 수 있습니다.
JWT는 다음과 같은 세 가지 구조로 구성됩니다.
Header
Payload
Signature
세 부분은 각각 Base64URL 방식으로 인코딩되어 있으며, 점(.)으로 구분되어 하나의 문자열 형태로 전달됩니다.
예를 들어 JWT 토큰은 다음과 같은 형태를 가집니다.
xxxxx.yyyyy.zzzzz
각 부분이 어떤 역할을 하는지 살펴보겠습니다.
JWT의 구조
Header
Header에는 토큰의 타입과 서명 알고리즘 정보가 포함됩니다.
일반적으로 다음과 같은 형태를 가집니다.
{
"alg": "HS256",
"typ": "JWT"
}
여기서 alg는 서명 알고리즘을 의미하며 HS256, RS256 등의 방식이 사용됩니다. typ은 토큰 타입을 나타내며 일반적으로 JWT로 설정됩니다.
Payload
Payload에는 실제 데이터가 포함됩니다. 이 영역에는 Claim이라고 불리는 정보들이 저장됩니다.
대표적인 Claim에는 다음과 같은 것들이 있습니다.
- 사용자 ID
- 권한 정보
- 토큰 발급 시간
- 토큰 만료 시간
예시 형태는 다음과 같습니다.
{
"sub": "123456",
"name": "user",
"iat": 1516239022
}
여기서 중요한 점은 Payload에 포함된 데이터는 암호화된 것이 아니라 단순히 인코딩된 데이터라는 것입니다. 따라서 민감한 정보를 직접 포함하는 것은 일반적으로 권장되지 않습니다.
Signature
Signature는 토큰의 위변조를 방지하기 위해 사용됩니다.
Signature는 다음 데이터를 기반으로 생성됩니다.
Base64UrlEncode(Header) + "." + Base64UrlEncode(Payload)
여기에 서버의 비밀키 또는 개인키를 이용하여 서명을 생성합니다.
서버는 요청을 받을 때 동일한 방식으로 Signature를 다시 계산하고, 토큰에 포함된 Signature와 비교하여 토큰의 무결성을 확인합니다.
이 과정을 통해 토큰이 중간에서 변조되지 않았음을 검증할 수 있습니다.
JWT 기반 인증 흐름
JWT 기반 인증 흐름은 비교적 단순한 구조를 가지고 있습니다.
먼저 사용자가 로그인 요청을 서버로 전달합니다. 서버는 사용자 인증을 수행한 뒤 JWT를 생성합니다.
생성된 JWT는 일반적으로 클라이언트에 전달됩니다. 이후 클라이언트는 API 요청을 보낼 때 이 토큰을 함께 전달합니다.
일반적으로 토큰은 다음과 같은 HTTP Header를 통해 전달됩니다.
Authorization: Bearer <token>
서버는 요청을 받을 때 토큰의 서명을 검증하고, 토큰에 포함된 Claim 정보를 통해 사용자 인증 상태를 확인합니다.
이 과정에서 서버는 별도의 Session 저장소를 조회할 필요가 없습니다. 토큰 자체에 필요한 정보가 포함되어 있기 때문입니다.
JWT의 장점
JWT 기반 인증 방식의 가장 큰 특징은 Stateless 구조입니다.
서버는 사용자 상태를 별도의 저장소에 보관하지 않아도 되며, 요청에 포함된 토큰만으로 인증 정보를 확인할 수 있습니다. 이 구조는 특히 서버가 여러 대로 확장되는 환경에서 유리하게 작동합니다.
서비스 인스턴스가 늘어나더라도 Session 동기화 문제를 고려할 필요가 없기 때문입니다. 이러한 특성 때문에 JWT는 수평 확장이 필요한 환경에서 자주 사용됩니다.
또한 마이크로서비스 아키텍처에서는 하나의 서비스에서 발급한 토큰을 다른 서비스에서도 검증하여 사용할 수 있습니다. 이를 통해 서비스 간 인증 정보를 비교적 단순하게 전달할 수 있습니다.
JWT의 단점
JWT는 여러 장점을 가지고 있지만 현실적인 한계도 존재합니다.
대표적인 문제 중 하나는 Token revoke가 어렵다는 점입니다. Session 기반 인증에서는 서버에서 Session을 삭제하면 즉시 로그아웃 처리가 가능합니다. 그러나 JWT는 클라이언트가 토큰을 보유하고 있는 동안 계속 사용할 수 있습니다.
또 다른 문제는 토큰 탈취 위험입니다. 공격자가 토큰을 획득하면 토큰 만료 전까지 인증된 사용자처럼 동작할 수 있습니다.
또한 토큰에는 일정한 크기가 있기 때문에 네트워크 요청마다 토큰이 전달되면 트래픽이 증가할 수 있습니다.
이러한 이유로 실제 서비스에서는 JWT를 사용할 때 추가적인 보완 전략을 함께 사용합니다.
Access Token과 Refresh Token 구조
JWT 기반 인증에서는 보통 Access Token과 Refresh Token을 함께 사용하는 구조가 많이 사용됩니다.
Access Token은 비교적 짧은 수명을 가지며 API 요청에 사용됩니다. Refresh Token은 더 긴 수명을 가지며 Access Token을 재발급하는 데 사용됩니다.
Access Token이 만료되면 클라이언트는 Refresh Token을 사용하여 새로운 Access Token을 요청합니다. 이 구조를 통해 토큰 탈취 위험을 일정 부분 줄일 수 있습니다.
또한 Refresh Token을 서버 측 저장소에 관리하면 특정 사용자의 토큰을 무효화하는 것도 어느 정도 가능해집니다.
OAuth 2.0과 API 인증 표준
현대 웹 서비스에서는 JWT가 단독으로 사용되기보다는 OAuth 2.0 같은 인증 프레임워크와 함께 사용되는 경우가 많습니다.
OAuth 2.0은 다양한 클라이언트 환경에서 안전하게 인증을 수행하기 위한 권한 위임 프레임워크입니다. 많은 인증 서버가 OAuth 2.0 기반 토큰을 발급할 때 JWT 형식을 사용합니다.
이와 함께 OpenID Connect 같은 프로토콜을 통해 사용자 인증 정보까지 확장하는 구조도 널리 사용됩니다.
현대 웹 아키텍처에서의 인증 구조
최근의 시스템 구조에서는 인증이 애플리케이션 내부의 기능을 넘어 인프라 레벨에서 처리되는 경우도 많습니다.
대표적인 구조 중 하나가 API Gateway 기반 인증입니다.
API Gateway는 클라이언트 요청이 내부 서비스로 전달되기 전에 JWT 토큰을 검증합니다. 토큰이 유효한 경우에만 요청을 내부 서비스로 전달합니다.
이 방식은 인증 로직을 중앙에서 관리할 수 있다는 장점이 있습니다.
또 다른 접근 방식은 BFF(Backend For Frontend) 패턴입니다. BFF는 특정 프론트엔드에 맞춘 백엔드 계층을 두어 인증과 API 호출을 처리합니다. 이 구조는 SPA 환경에서 보안 문제를 완화하는 데 도움이 될 수 있습니다.
마이크로서비스 환경에서는 서비스 간 호출에서도 토큰 기반 인증이 사용됩니다. 요청을 전달할 때 토큰을 함께 전달하여 각 서비스가 독립적으로 인증을 검증할 수 있도록 하는 방식입니다.
정리하며
웹 인증 방식의 흐름을 살펴보면 Cookie, Session, Token 기반 인증으로 이어지는 변화를 확인할 수 있습니다.
초기 웹에서는 클라이언트 저장 방식인 Cookie를 통해 상태를 유지하려 했고, 이후 서버가 상태를 직접 관리하는 Session 방식이 등장했습니다. 그러나 시스템 규모가 커지고 아키텍처가 분산되면서 Session 기반 구조 역시 여러 제약을 드러냈습니다.
이러한 환경에서 등장한 방식이 Token 기반 인증이며, JWT는 그 중에서 널리 사용되는 표준 중 하나입니다.
다만 JWT 역시 모든 상황에서 완벽한 해결책이 되는 것은 아닙니다. 실제 시스템에서는 Access Token과 Refresh Token 구조, OAuth 기반 인증, API Gateway 같은 다양한 전략이 함께 사용됩니다.
개인적으로는 인증 기술을 이해할 때 특정 기술 하나만 보는 것보다, 웹 아키텍처가 어떤 방향으로 발전해 왔는지를 함께 살펴보는 것이 더 도움이 된다고 느낍니다. Cookie에서 Session, 그리고 JWT로 이어지는 흐름 역시 결국 웹 시스템이 확장성과 분산 환경에 대응해 온 과정으로 볼 수 있기 때문입니다.
이러한 흐름을 이해해 두면 새로운 인증 기술이나 아키텍처를 접할 때도 조금 더 맥락을 가지고 바라볼 수 있을 것이라고 생각합니다.
'STUDY' 카테고리의 다른 글
- Total
- Today
- Yesterday
- Eager Initialization
- Cache Aside
- 백엔드 성능 설계
- 캐시 성능 비교
- Hot Key 문제
- 백엔드 아키텍처
- 스레드 생명주기
- Cache Penetration
- 백엔드 성능
- spring batch 5
- Spring Batch
- Java Performance
- Redis 성능 개선
- 트래픽 처리
- Cache Avalanche
- DB 트랜잭션
- TTL 설계
- Initialization-on-Demand Holder Idiom
- Redis 캐시 전략
- DB 인덱스 성능
- 백엔드 성능 튜닝
- 동시성처리
- Redis vs DB
- 트랜잭션 관리
- Double-Checked Locking
- InterruptedException
- 캐시 장애
- 캐시와 인덱스
- Enum 기반 싱글톤
- mybatis
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

