Spring Boot의 Filter에 대해

StrayCat·2026년 2월 22일

[Spring Boot] Filter - 요청/응답을 가로채는 관문

Spring Boot에서 Filter란 무엇인지, 어떻게 구현하고 활용하는지 정리합니다.


📌 Filter란?

Filter는 Web 애플리케이션(서블릿 컨테이너)에서 관리되는 영역으로,
클라이언트로부터 오는 요청(Request)응답(Response) 에 대해 최초/최종 단계에 위치합니다.

이를 통해 다음과 같은 작업을 수행할 수 있습니다.

  • 요청/응답 정보 변경
  • 로깅, 보안 처리 등 공통적으로 처리해야 하는 작업
  • 인증(Authentication) / 인가(Authorization) 로직 처리

💡 핵심 장점: 인증·인가 로직을 비즈니스 로직과 분리하여 관리할 수 있습니다.


🔗 Filter Chain

Filter는 단 하나만 존재하는 것이 아니라,
여러 개가 Chain 형태로 연결되어 순서대로 처리됩니다.

Client
  ↓
Filter₀  ← @Order(1)
  ↓
Filter₁  ← @Order(2)
  ↓
Filter₂
  ↓
Servlet (DispatcherServlet)

각 Filter는 chain.doFilter(request, response)를 호출하여 다음 Filter로 넘겨줍니다.
이 메서드를 호출하지 않으면 요청이 더 이상 진행되지 않습니다.


🛠️ Filter 구현하기

jakarta.servlet.Filter 인터페이스를 구현하면 됩니다.

기본 구조

public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // ✅ 전처리 (요청이 Servlet에 도달하기 전)
        // ... 요청 관련 로직

        chain.doFilter(request, response); // 다음 Filter 또는 Servlet으로 이동

        // ✅ 후처리 (Servlet 처리 완료 후, 응답이 클라이언트로 가기 전)
        // ... 응답 관련 로직
    }
}

chain.doFilter() 기준으로 위는 전처리, 아래는 후처리 영역입니다.


📝 실습 예제

1. LoggingFilter - 요청 URL 로깅

package com.mystudy.springauth.filter;

...

@Slf4j(topic = "LoggingFilter")
@Component           // Spring Bean으로 등록 → 자동으로 Filter로 인식됨
@Order(1)            // 필터 순서 지정 (낮을수록 먼저 실행)
public class LoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // 전처리: HttpServletRequest로 캐스팅하여 URL 추출
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();
        log.info("요청 URL: {}", url);

        chain.doFilter(request, response); // 다음 Filter로 이동

        // 후처리: 비즈니스 로직이 모두 완료된 후 실행됨
        log.info("비즈니스 로직 완료");
    }
}

포인트: log.info("비즈니스 로직 완료")chain.doFilter() 이후이므로,
Servlet 처리가 끝난 뒤 응답이 클라이언트로 가기 직전에 실행됩니다.


2. AuthFilter - JWT 인증/인가 처리 필터

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)  // LoggingFilter(1) 다음에 실행
public class AuthFilter implements Filter {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    // 생성자 주입 (AuthFilter는 JwtUtil과 UserRepository에 의존)
    public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        // ✅ 인증이 필요 없는 URL은 바로 통과
        if (StringUtils.hasText(url) &&
                (url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))) {
            // 회원가입, 로그인 관련 API 및 정적 리소스는 인증 없이 통과
            chain.doFilter(request, response);
        } else {
            // ✅ 그 외 모든 요청은 JWT 인증 처리
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);

            if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
                // 1. "Bearer " 접두어 제거
                String token = jwtUtil.substringToken(tokenValue);

                // 2. 토큰 유효성 검증
                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                // 3. 토큰에서 사용자 정보 추출
                Claims info = jwtUtil.getUserInfoFromToken(token);

                // 4. DB에서 사용자 조회
                User user = userRepository.findByUsername(info.getSubject())
                        .orElseThrow(() -> new NullPointerException("Not Found User"));

                // 5. request에 사용자 정보를 담아 다음 단계로 전달
                request.setAttribute("user", user);
                chain.doFilter(request, response);

            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }
}

AuthFilter 동작 흐름 요약:

요청 수신
  ↓
URL 확인
  ├─ /api/user, /css, /js → 인증 없이 통과
  └─ 그 외 URL
       ↓
     쿠키에서 JWT 토큰 추출
       ↓
     토큰 존재 여부 확인
       ├─ 없음 → IllegalArgumentException ("Not Found Token")
       └─ 있음
            ↓
          토큰 유효성 검증
            ├─ 실패 → IllegalArgumentException ("Token Error")
            └─ 성공
                 ↓
               토큰에서 username 추출 → DB 조회
                 ↓
               request에 User 객체 저장 → 다음 Filter로 이동

⚠️ Filter 등록 해제 방법

Filter로 등록하고 싶지 않을 때는 @Component 어노테이션을 주석 처리하면 됩니다.

@Slf4j(topic = "LoggingFilter")
// @Component  ← 주석 처리하면 Bean으로 등록되지 않아 Filter로 동작하지 않음
@Order(1)
public class LoggingFilter implements Filter {
    ...
}

🔑 핵심 어노테이션 정리

어노테이션설명
@ComponentSpring Bean으로 등록. Filter 인터페이스 구현체에 붙이면 자동으로 필터로 동작
@Order(n)필터 실행 순서 지정. 숫자가 낮을수록 먼저 실행됨

💡 추가로 알아두면 좋은 것

Filter vs Interceptor

FilterInterceptor
관리 주체서블릿 컨테이너 (Tomcat 등)Spring Context
적용 위치DispatcherServlet 이전DispatcherServlet 이후, Controller 이전
주요 용도인증/인가, 로깅, 인코딩 설정공통 비즈니스 로직, 권한 체크
Spring Bean 접근⚠️ 제한적 (Spring DI는 가능)✅ 자유롭게 접근 가능

인증·인가처럼 Spring 전역에 걸쳐 처리해야 하는 로직은 Filter,
Controller 전후 비즈니스 레벨의 처리Interceptor가 적합합니다.

request.setAttribute()를 활용한 데이터 전달

AuthFilter에서 인증된 User 객체를 request에 담아두면,
이후 Controller에서 꺼내어 사용할 수 있습니다.

// Filter에서 저장
request.setAttribute("user", user);

// Controller에서 꺼내기
User user = (User) request.getAttribute("user");
profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글