이 글에는 JWT가 무엇인지, 어떻게 동작하는지, 그리고 언제 사용하면 좋은지 정리했습니다.

시나리오 설정

이 글에서 다루는 예제는 대략 이런 환경을 기준으로 합니다.

  • 프론트엔드: React, Vue 로 만든 SPA
  • 백엔드: 스프링 부트 + 스프링 시큐리티 기반 API 서버
  • 도메인: SPA 는 https://app.example.com, API 서버는 https://api.example.com
  • 통신·인증: JSON 기반 REST API, Authorization: Bearer <액세스 토큰> 헤더 사용

이때 실제로 오가는 HTTP 요청은 아래와 같이 가정합니다.

구분메서드엔드포인트요청 바디 예시응답 바디 예시
로그인POST/api/auth/login{ "email": "user@example.com", "password": "pass" }{ "accessToken": "JWT...", "expiresIn": 3600 }
현재 사용자 조회GET/api/users/me(바디 없음, Authorization 헤더에 토큰){ "id": 1, "email": "user@example.com", "name": "사용자" }

유저 도메인 설계

기본 필드

유저 상태 (활성/비활성, 탈퇴, 잠금 여부 등)

권한/역할 구조 (ROLE_USER, ROLE_ADMIN 등)

UserDetails 구현

스프링 시큐리티 기본 설정

CORS 설정 (SPA 오리진 허용, 메서드·헤더 설정)

CSRF 전략 (SPA+JWT 환경에서 왜 disable 하는지, 예외 케이스)

세션 정책 (STATELESS 사용하는 이유)

JWT 구성

토큰에 무엇을 담을지(클레임 설계: sub, roles, 만료 시간 등)

JWT Provider/Util 설계

토큰 생성

토큰 검증 및 파싱

만료 시간 설정

인증 로직 추가

AuthenticationProvider 구성

UserDetailsService

PasswordEncoder

로그인 API (인증 성공 → JWT 발급 응답)

요청 검증: JWT 인증 필터

OncePerRequestFilter 기반 JWT 필터 구현

Authorization 헤더에서 토큰 꺼내기, 검증, SecurityContext에 저장

SecurityFilterChain에 필터 등록 위치

주의할 점

1. JWT 필터에서 예외를 throw하면 Spring의 기본 에러 핸들러가 처리한다

필터 체인은 DispatcherServlet 이전에 동작하므로, @ExceptionHandler가 동작하지 않습니다. 필터 안에서 직접 응답을 작성하거나, AuthenticationEntryPoint를 커스터마이징해야 합니다.

2. 토큰 파싱 실패 시 무조건 401을 반환하면 안 된다

토큰이 없는 요청(공개 API)과 토큰이 잘못된 요청을 구분해야 합니다. 토큰이 없으면 필터를 통과시키고(chain.doFilter), 토큰이 있지만 잘못되었으면 401을 반환합니다.

댓글 로딩 중...