세션 기반 인증에서 JWT 기반 인증으로 전환하면서 정리한 것들
배경
기존 프로젝트는 전형적인 세션 기반 인증 구조였다.
- 로그인 시 세션 생성
- 서버가 인증 상태를 기억
- 이후 요청은 세션으로 인증 처리
기능적으로는 문제 없었지만 구조적인 한계가 명확했다.
- 서버가 상태를 들고 있어야 함
- 확장 시 세션 공유 필요
- API 서버로 확장하기 어려움
그래서 Spring Security + JWT 기반 인증으로 전환했다.
세션 기반 인증의 특징
세션 기반 인증은 서버가 인증 상태를 직접 관리한다.
- 로그인 요청
- 서버에서 세션 생성
- 세션 ID를 쿠키로 전달
- 이후 요청마다 세션으로 인증
특징
- Stateful 구조
- 서버 메모리 또는 Redis 등 세션 저장소 필요
- 서버 확장 시 세션 동기화 문제 발생
JWT 기반 인증으로 전환
JWT는 서버가 상태를 저장하지 않는다.
인증 정보를 토큰에 담아 클라이언트에게 위임한다
흐름
- 로그인 요청
- 서버가 JWT 발급
- 클라이언트가 토큰 저장
- 요청 시 헤더에 포함
1
Authorization: Bearer <JWT>
- 서버는 토큰 검증 후 인증 처리
구현하면서 가장 크게 달라진 부분
가장 큰 변화는 인증이 처리되는 위치였다.
기존에는:
- Controller 이전에 세션 기반으로 인증 확인
JWT에서는:
- Filter 단계에서 인증 처리
JWT 필터 흐름
- 요청 진입
- Authorization 헤더 확인
- JWT 파싱 및 검증
- Authentication 생성
- SecurityContext에 저장
이 과정이 끝나면 컨트롤러에서는 이미 인증된 사용자로 인식된다.
여기서 중요했던 포인트
JWT를 적용하면서 가장 크게 느낀 건 이거였다.
인증은 Controller에서 시작되는 게 아니라, 그 이전에 이미 끝난다
즉,
- Controller → 비즈니스 로직 처리
- 인증/인가 → 그 전에 끝남
그래서 자연스럽게 생긴 의문
“그럼 이 필터는 언제 실행되는 거지?” “요청은 어디서부터 시작해서 Controller까지 오는 걸까?”
이걸 제대로 이해하려면 결국 Spring의 요청 처리 흐름을 알아야 한다.
- Filter Chain
- Spring Security
- DispatcherServlet
이 구조를 이해하지 못하면 JWT는 “동작은 하지만 왜 되는지는 모르는 상태”가 된다.
요청 흐름 정리 (핵심)
Spring의 요청은 다음 순서로 처리된다.
1
2
3
4
5
6
7
Client Request
↓
Filter Chain (Spring Security 포함)
↓
DispatcherServlet
↓
Controller
즉,
👉 JWT 인증은 DispatcherServlet에 도달하기 전에 이미 끝난다
관련 내용 정리한 글
이 부분을 따로 정리해둔 글이 있다.
→ 단순히 JWT를 적용하는 것보다 → 요청이 어떻게 흐르는지를 이해하는 게 훨씬 중요하다
👉 Spring Security 요청 처리 흐름과 DispatcherServlet
전환하면서 느낀 차이
1. 인증 책임의 이동
- 세션 → 서버가 상태 관리
- JWT → 클라이언트가 토큰 보관
2. Stateless 구조
- 서버가 인증 상태를 기억하지 않음
- 확장성 유리
3. 보안 책임 증가
- 토큰 탈취 시 그대로 인증 가능
- 만료 전략 필요 (Access / Refresh)
4. 로그아웃 방식 변화
세션:
- 세션 삭제 = 로그아웃
JWT:
- 서버가 상태를 모르기 때문에 즉시 무효화 어려움
- 별도 전략 필요 (Refresh 관리, 블랙리스트 등)
결론
세션 → JWT 전환은 단순한 기술 변경이 아니라 인증 구조 자체를 바꾸는 작업이었다.
- Stateful → Stateless
- 서버 중심 → 클라이언트 위임
그리고 이 과정에서 느낀 가장 중요한 포인트는 하나였다.
인증은 Controller가 아니라, 요청 흐름 초반(Filter)에서 이미 끝난다
한 줄 정리
JWT를 이해하려면 토큰보다 먼저 “요청 흐름”을 이해해야 한다.