Spring

Spring SecurityFilterChain 구조에 대해서 설명해주세요

HTTP 요청이 Spring Security를 통과하는 전체 흐름을 인터랙티브 시각화로 학습합니다. DelegatingFilterProxy → FilterChainProxy → SecurityFilterChain 구조와 주요 필터의 역할, JWT 기반 커스텀 설정 방법을 면접 답변 수준으로 정복합니다.

2026-03-22 · 약 14분 읽기

핵심 요약 (TL;DR)

  • — HTTP 요청은 DelegatingFilterProxy → FilterChainProxy → SecurityFilterChain 순서로 처리된다
  • FilterChainProxy는 여러 SecurityFilterChain을 가질 수 있으며, 요청 URL에 매칭되는 첫 번째 체인에 처리를 위임한다
  • ExceptionTranslationFilter는 인증 예외(401)와 인가 예외(403)를 HTTP 응답으로 변환하는 핵심 필터
  • — JWT 기반 REST API에서는 CSRF 비활성화 + Stateless 세션 정책 + 커스텀 필터 삽입이 기본 패턴

Q. "Spring Security의 SecurityFilterChain 구조를 설명해주세요. HTTP 요청이 DelegatingFilterProxy에서 실제 보안 필터까지 처리되는 흐름과, 주요 필터의 역할을 말씀해주세요."

예상 꼬리질문

답변 가이드

Spring Security의 핵심 구조는 DelegatingFilterProxy → FilterChainProxy → SecurityFilterChain 흐름입니다. 서블릿 컨테이너는 스프링 빈을 직접 알 수 없으므로, DelegatingFilterProxy가 서블릿 필터로 등록되어 실제 처리를 스프링 빈인 FilterChainProxy에 위임합니다. FilterChainProxy는 요청 URL 패턴에 따라 매칭되는 SecurityFilterChain을 선택하여 처리합니다.

SecurityFilterChain은 보안 처리를 담당하는 필터들이 순서대로 나열된 구조입니다. 요청이 들어오면 CsrfFilter(CSRF 검증), UsernamePasswordAuthenticationFilter(폼 로그인), AnonymousAuthenticationFilter(익명 사용자 처리), ExceptionTranslationFilter(보안 예외 변환), FilterSecurityInterceptor(최종 인가 결정) 순서로 처리됩니다.

실무에서 JWT 기반 REST API를 개발할 때는 세션을 사용하지 않으므로 SessionCreationPolicy.STATELESS를 설정하고 CSRF를 비활성화합니다. 그리고 JWT를 검증하는 커스텀 필터를 addFilterBefore()로 UsernamePasswordAuthenticationFilter 앞에 삽입합니다. 다중 SecurityFilterChain을 사용할 때는 반드시 @Order로 우선순위를 지정해야 합니다.

면접에서 "Spring Security 동작 원리"를 물었을 때 "로그인, 권한 관리를 해줍니다"라고 답하면 탈락입니다. 면접관이 정말 듣고 싶은 건 DelegatingFilterProxy에서 시작해서 어떤 필터가 어떤 순서로 실행되는지, 그리고 커스텀 설정을 어떻게 적용하는지입니다. 이 아티클에서는 필터 체인의 전체 구조를 인터랙티브 시각화로 직접 조작하며 면접 답변에 바로 쓸 수 있는 수준으로 익혀봅니다.


1. 전체 구조 — DelegatingFilterProxy에서 SecurityFilterChain까지

꼬리질문: "DelegatingFilterProxy는 왜 필요한가요? 없으면 어떻게 되나요?"

서블릿 컨테이너(Tomcat)는 스프링 빈을 모릅니다. 서블릿 필터는 서블릿 컨테이너가 관리하므로, 스프링이 직접 필터로 등록할 수 없는 구조적 한계가 있습니다. 이 문제를 해결하는 것이 DelegatingFilterProxy입니다. 서블릿 컨테이너에 등록된 표준 필터처럼 동작하면서, 실제 처리는 스프링 빈인 FilterChainProxy에 위임합니다.

FilterChainProxy(빈 이름: springSecurityFilterChain)는 여러 SecurityFilterChain을 가질 수 있으며, 요청 URL에 매칭되는 첫 번째 체인을 선택해 처리를 위임합니다. 이 구조 덕분에 /api/**와 /web/**에 완전히 다른 보안 정책을 적용할 수 있습니다.

아래에서 각 레이어를 클릭하면 역할 설명이 표시됩니다. "요청 흐름 재생" 버튼으로 전체 흐름을 애니메이션으로 확인해보세요.

필터 체인 계층 구조

레이어를 클릭하면
상세 설명이 표시됩니다

레이어 구분

서블릿 컨테이너 영역
스프링 컨텍스트 영역
애플리케이션 영역

2. 필터 실행 순서 — 어떤 필터가 어떤 순서로 실행되는가

꼬리질문: "UsernamePasswordAuthenticationFilter와 BasicAuthenticationFilter의 차이는 무엇인가요?"

SecurityFilterChain은 수십 개의 필터를 순서대로 실행합니다. 각 필터는 단일 책임을 가지며, 자신의 처리가 끝나면 다음 필터에 요청을 넘깁니다. 순서가 중요한 이유는 앞 필터의 처리 결과가 뒤 필터에 영향을 미치기 때문입니다.

설정에 따라 일부 필터는 활성화되지 않을 수 있습니다. JWT 방식에서는 UsernamePasswordAuthenticationFilter를 비활성화하고 커스텀 JWT 필터를 삽입합니다.

단계를 클릭하거나 버튼으로 이동하며 각 필터의 역할을 확인해보세요. JWT 모드에서 활성/비활성 여부도 표시됩니다.

실행 순서

단계 1 / 7

SecurityContextPersistenceFilter

#1

SecurityContext 로드/저장

역할 설명

요청 시작 시 HttpSession에서 SecurityContext를 로드하여 SecurityContextHolder에 설정합니다. 요청이 완료되면 SecurityContext를 다시 세션에 저장하고 ThreadLocal을 정리합니다.

JWT 모드: 비활성

Stateless 설정 시 SecurityContextHolderFilter로 대체됩니다. 세션을 사용하지 않으므로 로드/저장 동작이 생략됩니다.


3. 인증 처리 — UsernamePasswordAuthenticationFilter 내부 흐름

꼬리질문: "폼 로그인 요청이 들어왔을 때 내부에서 어떤 컴포넌트들이 호출되나요?"

인증(Authentication)은 "이 사용자가 누구인가"를 확인하는 과정입니다. UsernamePasswordAuthenticationFilter는 /login POST 요청에서 username과 password를 추출하여 AuthenticationManager에 검증을 위임합니다.

DaoAuthenticationProvider는 UserDetailsService.loadUserByUsername()으로 DB에서 사용자 정보를 가져온 뒤 PasswordEncoder로 비밀번호를 비교합니다. 인증 성공 시 SecurityContextHolder에 인증 정보를 저장하고, 실패 시 failureHandler가 호출됩니다.

버튼을 클릭하여 인증 성공/실패 시뮬레이션을 실행해보세요.

위의 버튼을 클릭하여 인증 흐름을 시뮬레이션하세요

4. 예외 처리 — ExceptionTranslationFilter의 두 가지 역할

꼬리질문: "ExceptionTranslationFilter가 처리하는 예외 두 가지는 무엇이고, 각각 어떻게 처리되나요?"

ExceptionTranslationFilter는 보안 예외를 사용자에게 적절한 HTTP 응답으로 변환합니다. 처리하는 예외는 두 종류입니다. AuthenticationException은 인증되지 않은 접근으로, 익명 사용자라면 로그인 페이지로 리다이렉트하거나 401 응답을 반환합니다. AccessDeniedException은 인증은 됐지만 권한이 없는 경우로, 인증된 사용자에게 403 응답을 반환합니다.

핵심 구분: 익명 사용자의 AccessDeniedException은 "인증이 필요한 것"이므로 AuthenticationEntryPoint(로그인 유도)로 처리하고, 인증된 사용자의 AccessDeniedException은 진짜 권한 부족이므로 AccessDeniedHandler(403)로 처리합니다.

탭을 클릭하여 세 가지 시나리오별 처리 흐름을 확인해보세요.

요청: GET /admin (인증 없음)

GET /admin 요청 (인증 없음)

AnonymousAuthenticationFilter

FilterSecurityInterceptor

AccessDeniedException 발생

ExceptionTranslationFilter

AuthenticationEntryPoint

302 리다이렉트 → /login

처리 결과

발생 예외

AccessDeniedException

처리 핸들러

AuthenticationEntryPoint

최종 결과

302 /login 리다이렉트

핵심 구분

익명 사용자 → AuthenticationEntryPoint (401/로그인)

인증된 사용자 → AccessDeniedHandler (403)


5. 커스텀 설정 — JWT REST API vs 세션 기반 웹

꼬리질문: "JWT 기반 REST API에서 SecurityFilterChain을 어떻게 설정하나요?"

SecurityFilterChain은 HttpSecurity로 커스텀 설정합니다. JWT 기반 REST API와 세션 기반 웹 애플리케이션은 설정 패턴이 크게 다릅니다. REST API는 무상태(Stateless)로 동작해야 하므로 세션을 사용하지 않고, CSRF 방어도 불필요합니다.

여러 SecurityFilterChain을 등록할 때는 @Order로 우선순위를 반드시 지정해야 합니다. securityMatcher()로 처리할 URL 범위를 제한하고, 숫자가 낮은 체인이 먼저 평가됩니다.

설정 항목을 클릭하면 상세 설명이 표시됩니다. "다중 체인 보기"로 통합 설정 예시를 확인하세요.

JWT 전용
세션 전용
공통

JWT REST API 설정

세션 기반 웹 설정


자주 발생하는 문제

실무에서 자주 마주치는 Spring Security 관련 문제와 해결 방법입니다.


마무리

  • DelegatingFilterProxy: 서블릿 컨테이너와 스프링 컨텍스트를 연결하는 브릿지 필터
  • FilterChainProxy: 여러 SecurityFilterChain 중 URL 패턴에 매칭되는 체인 선택
  • 필터 순서: CsrfFilter → UsernamePasswordAuthenticationFilter → AnonymousAuthenticationFilter → ExceptionTranslationFilter → FilterSecurityInterceptor
  • ExceptionTranslationFilter: AuthenticationException(401/로그인) vs AccessDeniedException(403) 분기 처리
  • JWT 설정 패턴: Stateless + CSRF 비활성화 + 커스텀 필터 삽입
  • — 더 알아볼 주제: OAuth2 ResourceServer 설정, Method Security(@PreAuthorize), Spring Security와 JWT 구현 심화

개념 확인 퀴즈

아티클에서 배운 개념을 실제 상황에 적용해봅니다.

퀴즈 1

필터 위임 구조

Spring Boot 애플리케이션에서 HTTP 요청이 들어올 때
보안 처리 순서로 올바른 것은?
퀴즈 2

ExceptionTranslationFilter 처리 분기

ROLE_USER로 로그인한 사용자가
hasRole("ADMIN") 설정된 /admin 페이지에 접근했습니다.
어떤 결과가 발생할까요?

.authorizeHttpRequests(auth -> auth
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated()
)
퀴즈 3

JWT 필터 삽입 위치

JwtAuthenticationFilter를 SecurityFilterChain에 추가하려 합니다.
다음 중 올바른 설정은?

// JwtAuthenticationFilter를 어디에 삽입해야 하는가?
http.addFilter???(jwtAuthenticationFilter, ???.class);
퀴즈 4

CSRF 비활성화 조건

다음 두 애플리케이션 중
CSRF를 비활성화해도 안전한 경우는?

A: 브라우저에서 세션 쿠키로 인증하는 전통적인 웹 애플리케이션

B: Authorization 헤더에 JWT Bearer 토큰으로 인증하는 REST API
퀴즈 5

다중 SecurityFilterChain 우선순위

두 개의 SecurityFilterChain이 등록되어 있습니다.
GET /api/users 요청은 어느 체인이 처리할까요?

@Bean
@Order(2)
public SecurityFilterChain webChain(HttpSecurity http) {
    // 폼 로그인, 세션 기반
    return http.build();
}

@Bean
@Order(1)
public SecurityFilterChain apiChain(HttpSecurity http) {
    http.securityMatcher("/api/**")
        .sessionManagement(s ->
            s.sessionCreationPolicy(STATELESS))
        .csrf(csrf -> csrf.disable());
    return http.build();
}

추가 학습 자료


의견을 들려주세요

서비스 개선에 큰 도움이 됩니다. 익명으로 자유롭게 남겨주세요.

0 / 1000