스프링 필터 대 인터셉터

MoonJaeGyeong·2024년 8월 28일

개요


스프링을 배우면서 유저의 로그인을 판단하는 로직을 짜려고 했을 때 내가 처음 떠올렸던 것은 바로 필터 이다. 필터로 걸러내면 되지 않겠나 했는데, 알고 보니 인터셉터 라는 것도 있다는 것을 알게 되었다.
뭔가 비슷해 보이는데 둘의 차이가 뭔지 탐구해보도록 하자



1. 필터


필터는 Client 의 요청이 디스패처 서블릿(Dispatcher Servlet) 에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대한 부가 작업을 처리할 수 있는 기능을 제공한다.

즉, 요청이 Spring 으로 가기 전인 Spring 밖에서 일처리가 진행된다는 것이다. 이를 그림으로 표현할 사진이 있다.


1.1 Filter 클래스

이를 자바에서 구현해놓은 클래스가 존재한다.

public interface Filter {

    public default void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

인터페이스의 구현해야하는 메소드를 살펴보면

  • init 메소드
  • doFilter 메소드
  • destroy 메소드

가 존재한다. 하나하나 살펴보자면

  1. init()
    필터 객체를 초기화 하고 서비스에 추가하기 위한 메소드, 웹 컨테이너가 1회 init()을 호출하여 필터 객체를 초기화한 이후 요청들은 doFileter()를 통해 처리된다.

  2. doFilter()
    url-pattern에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메소드이다.
    doFilter의 파라미터로 FilterChain 이 있는데, FilterChain.doFilter() 를 통해 다음 Filter로 연결시켜줄 수 있다.

  3. destroy()
    필터 객체를 제거하고 사용하는 자원을 반환하기 위한 메소드, 웹 컨테이너가 1회 destroy() 를 호출하여 필터 객체를 종료하면 이후에는 doFilter에 의해 처리되지 않는다.


1.2 스프링에서 Bean에 등록하기

public class CustomFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
@Configuration
public class Config{

    @Bean
    public FilterRegistrationBean customFilterRegister() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new CustomFilter());
        return registrationBean;
    }
}

@Configuration@Bean 어노테이션을 통해 Spring Bean에 등록이 가능하다.



2. 인터셉터


인터셉터는 Spring이 제공하는 기술로써, 디스패처 서블릿(Dispatcher Servlet) 이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다.

즉 웹 컨테이너에서 동작하는 필터와는 달리 인터셉터는 스프링 컨텍스트에서 동작한다.

디스패처 서블릿은 Client 에게 요청을 받은 후 이 요청을 처리할 알맞은 컨트롤러를 찾아주는데 이를 찾아주기 전에 인터셉터가 작동하는 것 이다.

이를 그림으로 표현하면 아래와 같다.


2.1 Interceptor 클래스

이를 스프링에서 구현해놓은 클래스는 다음과 같다.

public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }
}

인터페이스의 구현해야하는 메소드를 살펴보자면

  • preHandle 메소드
  • postHandle 메소드
  • afterCompletion 메소드
  1. preHandle 메소드
    컨트롤러가 호출되기 전에 실행되는 메소드, 컨트롤러 이전에 처리해야 하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우 사용 가능하다. preHandle 의 반환 타입은 boolean 인 걸 알 수 있는데 true 이면 다음 단계로 진행이 되지만, false 라면 작업을 중단하여 다음 작업을 진행하지 않는다.

  2. postHandle 메소드
    컨트롤러가 호출된 후에 실행되는 메소드, 컨트롤러 이후에 처리해야하는 후처리 작업이 있을 때 사용 가능하다. 컨트롤러 이후에 호출되어 컨트롤러 하위 계층에서 작업하다 예외 처리가 될 시 이 메소드는 실행되지 않는다.

  3. afterCompletion 메소드
    모든 작업이 완료된 후에 실행되는 메소드, 요청 처리 중에 사용한 리소스를 반환할 때 사용하기에 적합하다. postHandle 메소드와는 달리 중간에 예외가 발생하더라도 반드시 호출된다.



3. 필터 대 인터셉터

3.1 관리되는 컨테이너

필터와 인터셉터의 가장 큰 차이점이라고도 볼 수 있다. 서로 관리되는 컨테이너가 다른데 필터는 스프링 이전의 서블릿 영역에서 관리되지만, 인터셉터는 스프링 영역에서 관리된다. 이로 인해 필터는 스프링이 처리해주는 내용들을 적용받을 수 없다. 예를 들어 스프링에 의한 예외처리가 불가능하다.
참고로 관리되는 컨테이너가 필터는 스프링 밖이지만 빈으로는 등록이 가능하다. 뭔가 이상하다 생각되는데 이것이 가능한 이유는 다음에 다뤄보도록 하자.


3.2 Request/Response 객체 수정 가능 여부

필터는 Request와 Response를 조작할 수 있지만 인터셉터는 불가능하다. 여기서 조작한다는 것은 내부 상태를 변경한다는 내용이 아닌 다른 객체로 바꿔진다는 얘기이다. 이는 두 메소드를 살펴보면 알 수 있는데

public interface Filter {

    public default void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

필터의 경우에는 FilterChain 을 통해 다음 필터를 호출할 수 있는데 이 때 우리가 원하는 Request/Response 객체를 넣어줄 수 있다.

하지만 인터셉터의 경우

public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }
}

내부 로직을 통해 에러처리와 여러 데이터의 처리를 한 후 성공은 true로 실패는 false로 반환을 하여 다음 진행을 할 지 정해줄 뿐 request 와 response를 넘겨주는 코드가 없으므로 객체 수정이 불가능하다.


3.3 요약

대상필터인터셉터
관리되는 컨테이너서블릿 컨테이너스프링 컨테이너
Request/Response 객체 수정 가능 여부가능불가능
용도1. 공통된 보안 및 인증/인가 관련 작업
2. 모든 요청에 대한 로깅 또는 감사
3. 이미지/데이터 압축 및 문자열 인코딩
4. Spring과 분리되어야 하는 기능
1. 세부적인 보안 및 인증/ 인가 공통 작업
2. API 호출에 대한 로깅 또는 감사
3. Controller 로 넘겨주는 정보의 가공

profile
내 맘대로 끄적이는 개발 블로그

0개의 댓글