Spring
Spring Security JWT 인증 구현에 대해서 설명해주세요
JWT 구조(Header.Payload.Signature)부터 OncePerRequestFilter 구현, Access/Refresh Token 전략까지 — Spring Security 토큰 기반 인증의 모든 것을 인터랙티브 시각화로 완전 정복합니다.
2026-03-22 · 약 14분 읽기
Q. "Spring Security에서 JWT 인증을 어떻게 구현하는지 설명해주세요."
예상 꼬리질문
답변 가이드
JWT(JSON Web Token)는 Header.Payload.Signature 세 부분으로 구성된 자기 포함(Self-contained) 토큰입니다. 서버가 상태를 저장하지 않는 Stateless 인증을 가능하게 하며, 토큰 자체에 사용자 정보와 만료 시간이 포함됩니다.
Spring Security에서는 OncePerRequestFilter를 상속한 커스텀 필터로 JWT를 검증합니다. 요청의 Authorization 헤더에서 토큰을 추출하고, 서명을 검증한 뒤 SecurityContextHolder에 Authentication 객체를 저장하면 이후 컨트롤러에서 인증된 사용자로 처리됩니다.
보안 강화를 위해 Access Token(단기, 30분)과 Refresh Token(장기, 7일)을 함께 사용합니다. Access Token이 만료되면 Refresh Token으로 재발급하고, Refresh Token Rotation으로 탈취 시 즉시 감지합니다. Refresh Token은 DB 또는 Redis에 저장해 서버에서 무효화를 제어합니다.
면접에서 "JWT 인증을 구현해본 적 있나요?"라는 질문은 단순히 라이브러리 사용 경험을 묻는 게 아닙니다. JWT 구조의 원리, Spring Security 필터 체인과의 결합 방식, 그리고 Access/Refresh Token 전략의 보안 설계까지 이해하고 있는지를 확인합니다. 이 아티클에서 각 개념을 인터랙티브 시각화로 체득해 보겠습니다.
1. JWT 구조 — Header.Payload.Signature 해부
꼬리질문: "JWT의 Header, Payload, Signature는 각각 어떤 역할을 하나요?"
JWT는 점(.)으로 구분된 세 파트로 구성됩니다. 각 파트는 Base64Url로 인코딩되어 있으며, 서버는 Signature를 비밀키로 검증해 토큰의 위변조 여부를 판단합니다.
Payload는 암호화가 아닌 인코딩이므로 누구나 디코딩해서 읽을 수 있습니다 — 민감 정보(비밀번호 등)를 절대 넣으면 안 되는 이유입니다. 아래에서 각 파트를 클릭해 직접 디코딩 내용을 확인해보세요.
JWT 구조 탐색기
각 파트를 클릭해 내용을 확인하세요.
Header 디코딩 결과
{
"alg": "HS256",
"typ": "JWT"
}사용할 서명 알고리즘(HS256)과 토큰 타입(JWT)을 명시합니다. Base64Url로 인코딩되어 있습니다.
iat(issuedAt)와 exp(expiration)는 Unix 타임스탬프(초 단위)로 표현됩니다.exp가 현재 시간보다 작으면 토큰이 만료된 것이며, 서버는 이를 자동으로 검증합니다.
2. 세션 vs JWT — 인증 방식 비교
꼬리질문: "세션 기반 인증과 JWT 기반 인증의 차이점은 무엇인가요?"
세션 기반 인증은 서버가 로그인 상태를 직접 저장합니다(Stateful). 반면 JWT는 토큰 자체에 정보를 담아 서버가 상태를 저장하지 않습니다(Stateless).
이 차이가 수평 확장, 보안 특성, 토큰 무효화 방식 전반에 걸쳐 근본적인 트레이드오프를 만들어냅니다. 아래 탭에서 시나리오별 차이를 확인해보세요.
세션 vs JWT 비교
시나리오별로 두 방식의 차이를 확인하세요.
세션 기반
JWT 기반
| 항목 | 세션 | JWT |
|---|---|---|
| 서버 상태 | ⚠ Stateful | ✓ Stateless |
| 확장성 | ⚠ 어려움 | ✓ 용이 |
| 즉시 무효화 | ✓ 가능 | ⚠ 만료 대기 |
| 네트워크 크기 | ✓ 작음 (세션ID) | ⚠ 큼 (토큰) |
| 보안 제어 | ✓ 서버 완전 제어 | ⚠ 클라이언트 보관 |
3. Spring Security 필터 체인과 JWT 필터
꼬리질문: "OncePerRequestFilter를 사용하는 이유가 무엇인가요?"
Spring Security는 서블릿 필터 체인으로 동작합니다. JWT 인증을 추가하려면 OncePerRequestFilter를 구현한 커스텀 필터를 UsernamePasswordAuthenticationFilter 앞에 등록합니다.
OncePerRequestFilter를 사용하는 이유는 요청당 정확히 한 번만 실행을 보장하기 위해서입니다. 포워드/인클루드 시 중복 실행을 방지해 불필요한 DB 조회를 막습니다. 시뮬레이션으로 각 시나리오의 필터 통과 흐름을 확인해보세요.
JWT 필터 체인 시뮬레이터
시나리오를 선택하고 시뮬레이션을 시작하세요.
SecurityContextPersistenceFilter
SecurityContext를 요청 시작에 복원
JwtAuthenticationFilter ★
JWT 추출 → 검증 → SecurityContextHolder 저장
UsernamePasswordAuthFilter
폼 로그인 처리 (REST API에서는 SKIP)
ExceptionTranslationFilter
인증/인가 예외를 401/403 응답으로 변환
FilterSecurityInterceptor
최종 권한 검사 후 접근 허용/거부
SecurityContextHolder에 Authentication이 저장되면, 이후 @AuthenticationPrincipal UserDetails user로 컨트롤러에서 현재 인증된 사용자 정보를 바로 꺼낼 수 있습니다.
4. Access Token + Refresh Token 전략
꼬리질문: "Access Token과 Refresh Token을 함께 사용하는 이유가 무엇인가요?"
Access Token만 사용하면 만료 전 무효화가 불가능합니다. 토큰 수명을 길게 설정하면 탈취 위험이 높아지고, 짧게 설정하면 사용자가 자주 재로그인해야 합니다.
두 토큰을 함께 사용하면 이 딜레마를 해결합니다. Access Token(30분)이 만료되면 Refresh Token(7일)으로 조용히 재발급하고, Refresh Token Rotation으로 탈취를 즉시 감지합니다.
토큰 생명주기 시뮬레이터
Access Token과 Refresh Token의 생명주기를 확인하세요.
5. 토큰 저장 위치와 보안 전략
꼬리질문: "JWT를 어디에 저장하는 것이 가장 안전한가요?"
"토큰을 어디에 저장하느냐"는 보안과 편의성의 트레이드오프 문제입니다.localStorage는 구현이 쉽지만 XSS 공격에 취약합니다.HttpOnly 쿠키는 JavaScript 접근이 차단되지만 CSRF 공격을 별도로 방어해야 합니다.
각 방식의 위협 모델을 이해하고 상황에 맞는 전략을 선택해야 합니다. 각 저장 방식 카드를 클릭해 보안 특성을 비교해보세요.
토큰 저장 위치 보안 비교
저장 방식을 클릭해 보안 특성을 확인하세요.
메모리 (React state)
변수에만 존재하므로 XSS 공격도 접근할 수 없습니다. 새로고침 시 사라지지만, Refresh Token으로 재발급하면 됩니다. Access Token 저장에 가장 안전합니다.
실무 권장 전략
Access Token → 메모리(React state) | Refresh Token → HttpOnly 쿠키 + 서버 DB/Redis
자주 발생하는 문제
JWT 인증 구현 시 실무에서 마주치는 대표적인 보안 실수와 해결책입니다. 항목을 클릭해 상세 내용을 확인하세요.
면접 체크리스트
이 항목들을 모두 설명할 수 있다면 면접 준비 완료입니다.
- - JWT 구조: Header(알고리즘) + Payload(클레임, 암호화 아님) + Signature(위변조 방지)
- - Spring Security 연동: OncePerRequestFilter 상속 → 토큰 추출 → 검증 → SecurityContextHolder 저장
- - 두 토큰 전략: Access Token(단기, 인증) + Refresh Token(장기, 재발급) — 보안과 UX의 균형
- - Refresh Token Rotation: 갱신마다 새 RT 발급, 재사용 감지 → 전체 세션 무효화
- - 저장 위치: Access Token = 메모리, Refresh Token = HttpOnly 쿠키 + DB
- - 더 알아볼 주제: OAuth 2.0 / OpenID Connect, Spring Security OAuth2 Resource Server, JWT 블랙리스트 패턴
JWT Payload 보안
String token = Jwts.builder()
.claim("username", "user123")
.claim("password", "secret1234") // 비밀번호 포함
.claim("role", "ADMIN")
.expiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(key)
.compact();OncePerRequestFilter vs Filter
// 방식 A
@Component
public class JwtFilter implements Filter {
@Override
public void doFilter(ServletRequest req,
ServletResponse res, FilterChain chain) { ... }
}
// 방식 B
@Component
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) { ... }
}Refresh Token 저장 위치
// 모바일 앱과 웹 모두 지원하는 서비스를 개발 중입니다. // 로그아웃 시 즉시 토큰을 무효화해야 하고 보안이 중요합니다. // Refresh Token을 어디에 저장해야 할까요?
JWT 필터 체인 등록 위치
// JwtAuthenticationFilter는 어느 필터 앞에 등록해야 할까요?
http.addFilterBefore(
jwtFilter,
/* 어느 필터? */
);Refresh Token Rotation
// 사용자 A의 Refresh Token이 탈취되어 // 공격자가 먼저 /auth/refresh를 호출해 새 Access Token을 받았습니다. // 이후 원래 사용자 A가 동일한 Refresh Token으로 갱신을 시도합니다. // Rotation이 적용된 경우 어떻게 될까요?
참고 자료
- JWT.io — JWT 디버거 — 실제 JWT 토큰을 붙여넣으면 Header/Payload를 즉시 디코딩해주는 공식 디버거. 구조 이해에 필수
- Baeldung — Spring Security with JWT Authentication — 가장 체계적인 Spring Security + JWT 통합 가이드. 코드 예제 중심으로 단계별 구현 방법을 설명
- 우아한 형제들 기술 블로그 — 토큰 기반 인증 — 실무에서 JWT 도입 시 고려사항을 한국어로 설명한 글. Access/Refresh Token 전략과 보안 이슈를 다룸
의견을 들려주세요
서비스 개선에 큰 도움이 됩니다. 익명으로 자유롭게 남겨주세요.