본문 바로가기

MVC

[MVC] 필터와 인터셉터

목차

  1. 서블릿 필터
  2. 스프링 인터셉터

서블릿 필터와 스프링 인터셉터 

웹 애플리케이션에서 요청과 응답을 가로채어 특정 로직을 수행해야 하는 경우가 많다.

이를 위해 서블릿 필터(Servlet Filter)스프링 인터셉터(Spring Interceptor) 를 사용할 수 있다.

두 개념은 비슷하지만 동작 방식과 적용 범위에서 차이가 있다.

비교 항목 서블릿 필터 (Servlet Filter) 스프링 인터셉터 (Spring Interceptor)
적용 대상 모든 요청 (정적 리소스 포함) 스프링 MVC 컨트롤러 요청
실행 시점 서블릿 실행 전후 컨트롤러 실행 전후
주요 메서드 init(), doFilter(), destroy() preHandle(), postHandle(), afterCompletion()
등록 방식 web.xml 또는 @Component WebMvcConfigurer를 이용한 등록
주된 사용 목적 인증, 로깅, CORS 처리 등 인증, 로깅, 데이터 가공 등

🥷 서블릿 필터 (Servlet Filter)

서블릿 필터는 클라이언트 요청을 가로채어 특정 작업을 수행할 수 있는 기능을 제공한다.

모든 요청과 응답을 처리할 수 있으며, 정적 리소스(예: CSS, JS, 이미지)에도 적용된다.

 

1️⃣ 서블릿 필터 동작 방식

  1. 클라이언트가 요청을 보냄
  2. 필터가 요청을 가로채어 처리
  3. 서블릿이 요청을 처리
  4. 필터가 응답을 가로채어 처리
  5. 클라이언트에게 응답 반환

2️⃣ 주요 메서드 및 설명

@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("필터 초기화");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("요청이 필터를 통과함: " + ((HttpServletRequest) request).getRequestURI());
        chain.doFilter(request, response); // 다음 필터 또는 서블릿 호출
        System.out.println("응답이 필터를 통과함");
    }
    
    @Override
    public void destroy() {
        System.out.println("필터 제거");
    }
}

 

1) init(FilterConfig filterConfig)

  • 필터가 초기화될 때 실행되는 메서드
  • 필터의 설정 정보를 로드하거나 초기 자원을 할당하는 역할

2) doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

  • HTTP 요청이 오면 doFilter 가 호출된다
  • 필터의 핵심 메서드로, 요청과 응답을 가로채어 처리
  • chain.doFilter(request, response)를 호출하면 다음 필터 또는 서블릿으로 요청이 전달됨

3) destroy()

  • 필터가 제거될 때 실행되는 메서드
  • 사용한 자원을 해제하는 역할

3️⃣ FilterRegistrationBean을 사용한 필터 등록 방법

 

1. 필터 클래스 생성:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

public class ExampleFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Before request processing");
        chain.doFilter(request, response);  // 다음 필터로 요청을 전달
        System.out.println("After request processing");
    }

    @Override
    public void destroy() {
        // 필터 정리 작업
    }
}
  • 필터 클래스는 javax.servlet.Filter 인터페이스를 구현해야 한다.

2. FilterRegistrationBean을 사용한 필터 등록:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean<ExampleFilter> exampleFilter() {
        FilterRegistrationBean<ExampleFilter> registrationBean = new FilterRegistrationBean<>();
        // 등록할 필터 인스턴스를 설정
        registrationBean.setFilter(new ExampleFilter()); 
        // 필터가 적용될 URL 패턴 설정, /api/*로 설정하면 /api/로 시작하는 URL에만 필터가 적용
        registrationBean.addUrlPatterns("/api/*");
        // 필터 실행 순서 설정 (숫자가 낮을수록 우선순위가 높음), 여러 개의 필터가 있을 경우 이 값에 따라 실행 순서가 결정
        registrationBean.setOrder(1);  
        return registrationBean;
    }
}
  • 필터를 FilterRegistrationBean으로 등록하려면 WebConfig 클래스에 @Bean을 추가한다.
  • FilterRegistrationBean은 스프링에서 서블릿 필터를 등록하는 데 사용되는 편리한 방법이다.
  • 이 방법을 사용하면 web.xml 파일에 등록하지 않고도 필터를 JavaConfig에서 설정할 수 있다.
  • 또한, 필터의 순서나 특정 URL 패턴에 대한 필터 적용 등을 세밀하게 설정할 수 있다.

✅ 장점

  • @Bean 방식으로 스프링 컨텍스트 내에서 필터를 관리하므로, DI를 통한 필터 의존성 주입이 가능해진다.
  • 필터와 관련된 설정을 스프링 환경에 맞게 더 세밀하게 관리할 수 있다.

❌ 단점

  • @WebFilter 애너테이션을 사용하는 방식보다 설정이 더 복잡할 수 있다.
  • 또한, 필터 체인을 좀 더 세밀하게 다뤄야 할 경우 유용하지만, 간단한 경우에는 오히려 과도한 설정이 될 수 있다.

4️⃣ @WebFilter 애너테이션을 사용한 필터 등록 (서블릿 3.0 이상)

 

1. 필터 클래스에 @WebFilter 애너테이션 추가:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/api/*")  // 필터가 적용될 URL 패턴
public class ExampleFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Before request processing");
        chain.doFilter(request, response);  // 다음 필터로 요청을 전달
        System.out.println("After request processing");
    }

    @Override
    public void destroy() {
        // 필터 정리 작업
    }
}

 

  • @WebFilter 애너테이션을 사용하여 필터 클래스를 선언할 수 있다.
  • 또한, urlPatterns 속성을 사용하여 필터가 적용될 URL 패턴을 지정한다.

2. @ServletComponentScan 애너테이션 사용 (스프링 부트의 경우)

import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@ServletComponentScan  // @WebFilter가 있는 클래스를 자동으로 등록
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

 

  • 스프링 부트에서는 @ServletComponentScan 애너테이션을 사용하여 서블릿, 필터, 리스너를 자동으로 스캔하여 등록할 수 있다.
  • 이 애너테이션은 @SpringBootApplication 애너테이션이 있는 클래스에 추가하면 된다.

✅ 장점

  • 코드 기반으로 필터를 선언하고, 별도로 web.xml이나 FilterRegistrationBean을 설정하지 않아도 된다.
  • 스프링 부트와 함께 사용할 때 유용하며, 코드가 간결하고 관리가 쉬워진다.

❌ 단점

  • 필터에 추가적인 설정(순서, 특정 URL 패턴에 대한 추가 설정 등)이 필요한 경우, FilterRegistrationBean을 사용하는 것이 더 유연하다.

5️⃣ 필터 체인

public class ExampleFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Before request processing");
        chain.doFilter(request, response);  // 다음 필터로 요청을 전달
        
        // 다음 필터나 서블릿에서 응답을 처리하고 돌아올 때 실행됨.
        System.out.println("After request processing"); 
    }
}
  • 필터 체인은 여러 개의 필터가 연결되어 순차적으로 실행되는 구조이다.
  • FilterChain 객체는 필터들이 처리한 요청을 다음 필터로 전달하며, 마지막 필터는 서블릿으로 요청을 전달한다.
  • 각 필터는 doFilter 메서드에서 chain.doFilter(request, response)를 호출하여 다음 필터로 요청을 넘긴다.

🍃 스프링 인터셉터 (Spring Interceptor)

스프링 인터셉터(Spring Interceptor)는 Spring MVC의 요청 처리 과정에서 특정한 지점에 개입하여 요청을 처리할 수 있는 기능을 제공한다. 주로 요청 전후로 특정 작업을 수행하거나, 컨트롤러 메서드 호출 전에 처리하고자 할 때 사용된다. 서블릿 필터와 달리 스프링 인터셉터는 주로 스프링의 DispatcherServlet 처리 과정에 통합되어 동작한다.

 

1️⃣ 주요 메서드 및 설명

public class LoggingInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response, 
    Object handler) {
        System.out.println("요청 URL: " + request.getRequestURI());
        return true; // true여야 요청이 계속 진행됨
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
    Object handler, ModelAndView modelAndView) {
        System.out.println("컨트롤러 실행 후");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
    Object handler, Exception ex) {
        System.out.println("응답 완료");
    }
}

 

1) preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

  • 컨트롤러 실행 전에 호출됨
  • 요청을 검사하고 특정 조건에 따라 요청을 차단할 수 있음 (false 반환 시 요청 중단)

2) postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)

  • 컨트롤러 실행 후, 뷰가 렌더링되기 전에 호출됨
  • 응답 데이터 수정, 추가적인 로직 수행 가능

3) afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

  • 뷰가 렌더링된 후 호출됨
  • 리소스 정리 및 예외 처리 가능

2️⃣ 인터셉터 등록 방법

 

1. HandlerInterceptor 인터페이스 구현

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ExampleInterceptor implements HandlerInterceptor {

    // 요청이 컨트롤러로 넘어가기 전에 호출됩니다.
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Before handling the request");
        return true; // true를 반환하면 다음 인터셉터나 컨트롤러로 넘어감
    }

    // 컨트롤러 실행 후, 뷰를 렌더링 하기 전에 호출됩니다.
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
        System.out.println("After handling the request but before rendering the view");
    }

    // 뷰 렌더링 후에 호출됩니다. 예외 처리도 이곳에서 가능합니다.
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("After the request has been completed");
    }
}
  • 스프링 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현해야 한다.
  • 이 인터페이스는 세 가지 주요 메서드를 제공한다.

2. WebMvcConfigurer를 사용한 인터셉터 등록

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ExampleInterceptor())  // 인터셉터 등록
                .addPathPatterns("/api/*")  // 인터셉터가 적용될 URL 패턴
                .excludePathPatterns("/api/ignore/*") // 제외할 URL 패턴
                .order(0);// 인터셉터의 순서를 설정, 낮은 숫자는 높은 우선순위를 의미(기본값: 0)
    }
}
  • 스프링에서는 인터셉터를 WebMvcConfigurer를 통해 등록할 수 있다.
  • WebMvcConfigurer 인터페이스를 구현한 클래스에 addInterceptors() 메서드를 오버라이드하여 인터셉터를 설정한다.

3. @Component를 사용한 자동 등록

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class ExampleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Before handling the request");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
        System.out.println("After handling the request but before rendering the view");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("After the request has been completed");
    }
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final ExampleInterceptor exampleInterceptor;

    public WebConfig(ExampleInterceptor exampleInterceptor) {
        this.exampleInterceptor = exampleInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(exampleInterceptor)
                .addPathPatterns("/api/*");
    }
}
  • 인터셉터는 @Component 애너테이션을 사용하여 스프링 빈으로 등록할 수 있다.
  • 이 경우 WebMvcConfigurer에서 addInterceptor() 메서드를 사용하여 자동으로 등록할 수 있다.
  • @Component로 선언된 인터셉터는 스프링의 빈으로 자동 등록되며, WebConfig에서 직접 @Bean을 사용할 필요 없이 스프링이 자동으로 관리한다.

3️⃣ 스프링 인터셉터 호출 흐름

  • preHandle : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출)
  • preHandle 의 응답값이 true 이면 다음으로 진행하고, false 이면 더는 진행하지 않는다. false 인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝이 나버린다.
  • postHandle : 컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출)
  • afterCompletion : 뷰가 렌더링 된 이후에 호출된다.

4️⃣ 스프링 인터셉터 예외 상황

예외가 발생시

  • preHandle : 컨트롤러 호출 전에 호출된다.
  • postHandle : 컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
  • afterCompletion : afterCompletion 은 항상 호출된다. 이 경우 예외(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.

afterCompletion은 예외가 발생해도 호출된다.

  • 예외가 발생하면 postHandle() 는 호출되지 않으므로 예외와 무관하게 공통 처리를 하려면 afterCompletion() 을 사용해야 한다.
  • 예외가 발생하면 afterCompletion() 에 예외 정보(ex)를 포함해서 호출된다.

 

'MVC' 카테고리의 다른 글

[MVC] Spring MVC와 Dispatcher Servlet  (0) 2025.02.13
[Spring] Spring MVC vs Spring WebFlux  (1) 2024.12.05
[MVC] 파일 업로드  (0) 2024.08.06
[MVC] 스프링 타입 컨버터  (0) 2024.08.05
[MVC] API 예외 처리 @ExceptionHandler  (0) 2024.08.02