프레임워크/스프링

Filter vs Interceptor: 둘 다 거름망인데 도대체 뭐가 다를까?

eodevelop 2025. 12. 22. 22:38
반응형

Spring으로 웹 애플리케이션을 개발하다 보면 Filter와 Interceptor를 자주 마주칩니다. 둘 다 요청을 가로채서 뭔가를 처리한다는 점에서 비슷해 보입니다. 인증 처리를 해야 할 때, 로깅을 해야 할 때, 어떤 걸 써야 할지 고민이 될 때가 있습니다. 왜 두 가지가 따로 존재하는지, 각각 언제 사용하면 좋은지 정리해보겠습니다.


핵심 차이: 누구의 영역인가

결론부터 말하면, Filter는 Servlet의 영역이고, Interceptor는 Spring의 영역입니다.

이 흐름을 보면 감이 올 겁니다. Filter가 먼저 실행되고, 그 다음에 Spring 영역으로 들어와서 Interceptor가 실행됩니다.


Filter: Servlet 스펙

Filter는 Java Servlet 스펙에 정의된 기능입니다. Spring이 아니라 Servlet Container(Tomcat 등)가 관리합니다.

특징

  • jakarta.servlet.Filter 인터페이스를 구현 (Spring Boot 2.x 이하는 javax.servlet.Filter)
  • DispatcherServlet에 요청이 도달하기 전/후에 동작
  • Spring Boot에서는 @Component로 등록하면 Bean 주입 가능
  • 단, DispatcherServlet 이전에 실행되므로 @ControllerAdvice 등 Spring MVC 기능은 사용 불가
  • 모든 요청에 대해 동작 (정적 리소스 포함)

코드 예시

@Component
public class LoggingFilter implements Filter {

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

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        System.out.println("[Filter] 요청 URI: " + httpRequest.getRequestURI());

        // 다음 필터 또는 서블릿으로 요청 전달
        chain.doFilter(request, response);

        System.out.println("[Filter] 응답 완료");
    }
}

Filter가 적합한 경우

  • 인코딩 변환: 모든 요청의 문자 인코딩을 UTF-8로 설정
  • XSS 방어: 요청 파라미터의 악성 스크립트 필터링
  • CORS 처리: Cross-Origin 요청 헤더 처리
  • 요청/응답 로깅: Spring과 무관하게 모든 요청을 기록
@Component
public class EncodingFilter implements Filter {

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

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        chain.doFilter(request, response);
    }
}

Interceptor: Spring MVC 스펙

Interceptor는 Spring MVC가 제공하는 기능입니다. Spring Context 내부에서 동작하기 때문에 Spring의 모든 기능을 활용할 수 있습니다.

특징

  • HandlerInterceptor 인터페이스를 구현
  • DispatcherServlet이 Controller를 호출하기 전/후에 동작
  • Spring Bean을 주입받아 사용 가능
  • Controller에 대한 요청만 처리 (정적 리소스는 제외 가능)

코드 예시

@Component
public class AuthInterceptor implements HandlerInterceptor {

    private final TokenService tokenService;  // Spring Bean 주입 가능!

    public AuthInterceptor(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {

        String token = request.getHeader("Authorization");

        if (!tokenService.isValid(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;  // false를 반환하면 Controller까지 가지 않음
        }

        return true;  // true를 반환해야 다음 단계로 진행
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        // Controller 실행 후, View 렌더링 전
        System.out.println("[Interceptor] Controller 처리 완료");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        // View 렌더링까지 완료된 후
        System.out.println("[Interceptor] 요청 처리 완전히 완료");
    }
}

Interceptor 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final AuthInterceptor authInterceptor;

    public WebConfig(AuthInterceptor authInterceptor) {
        this.authInterceptor = authInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")        // 적용할 경로
                .excludePathPatterns("/api/auth/**");  // 제외할 경로
    }
}

Interceptor가 적합한 경우

  • 인증/인가: 로그인 여부, 권한 확인
  • 로그인 체크: 특정 페이지 접근 전 로그인 상태 확인
  • API 호출 로깅: Controller 단위의 요청/응답 기록
  • 공통 데이터 설정: 모든 Controller에 필요한 공통 데이터 주입

실행 흐름 상세

요청이 들어올 때의 전체 흐름을 보겠습니다.

 

예외가 발생했을 때도 차이가 있습니다.

  • Filter: DispatcherServlet 밖이라 @ControllerAdvice가 예외를 잡지 못함. Filter 내에서 직접 try-catch로 처리해야 함
  • Interceptor: Spring 내부라서 @ControllerAdvice로 예외를 처리할 수 있음. afterCompletion은 예외 발생 여부와 관계없이 실행됨

비교 정리

구분 Filter Interceptor
스펙 Servlet Spring MVC
관리 주체 Servlet Container Spring Container
실행 시점 DispatcherServlet 전/후 Controller 전/후
Spring Bean 주입 가능 (Spring Boot 기준) 가능
Spring MVC 기능 사용 불가 (@ControllerAdvice 등) 사용 가능
Request/Response 조작 가능 (래퍼 클래스 사용) 제한적

그래서 뭘 써야 할까?

간단하게 정리하면 이렇습니다.

Filter를 선택하는 경우:

  • Spring과 무관한 순수 Servlet 레벨의 처리
  • 모든 요청(정적 리소스 포함)에 대한 처리
  • Request/Response 자체를 변경해야 할 때 (래퍼 클래스 사용)
  • 인코딩, 보안(XSS, CORS) 등 웹 애플리케이션 공통 관심사

Interceptor를 선택하는 경우:

  • Controller 단위의 세밀한 제어가 필요할 때
  • @ControllerAdvice와 연계한 예외 처리가 필요할 때
  • 간단한 인증/인가 처리 (세션 체크, JWT 검증 등)
  • API 요청에 대한 로깅, 모니터링

참고로 Spring Security는 Filter 기반으로 동작합니다. 본격적인 보안 처리가 필요하다면 Spring Security를 사용하는 것이 일반적입니다.

실제로 많이 쓰는 조합을 예로 들면:

Filter: 인코딩 설정, CORS, XSS 필터링
Interceptor: 로그인 체크, API 인증, 요청 로깅

 

둘은 경쟁 관계가 아니라 각자의 역할이 있는 것입니다. Spring 영역 밖에서 처리해야 하면 Filter, Spring 기능을 활용해야 하면 Interceptor를 선택하면 됩니다.


정리

  • Filter: Servlet 스펙, DispatcherServlet 이전에 동작, 모든 요청 대상
  • Interceptor: Spring MVC 스펙, DispatcherServlet 이후에 동작, Controller 요청 대상
  • 둘 다 Spring Boot에서는 Bean 주입이 가능하지만, Filter는 @ControllerAdvice 같은 Spring MVC 기능을 사용할 수 없다
  • Request/Response 자체를 조작하거나 모든 요청을 처리해야 하면 Filter
  • Controller 단위 제어나 Spring MVC 기능 연계가 필요하면 Interceptor
  • 둘 다 같이 사용해도 된다. 역할에 맞게 선택하면 된다
반응형