본문 바로가기
Spring

Spring - Filter / Interceptor

by icblue21 2022. 11. 23.
728x90

필터와 인터셉터 이 둘은 모두 웹과 관련된 공통 관심사를 처리할 때 사용한다.
그렇다면 이 둘은 뭐가 다른 것일까? 이번 포스트에서는 필터와 인터셉터에 대해 알아보겠다.


Filter

필터는 J2EE 표준 스펙 기능으로 정확한 이름은 서블릿 필터이다. Dispatcher Servlet에 요청이 전달되기 전후에 URL패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공한다. 따라서 스프링 컨테이너가 아닌 WAS같은 웹 컨테이너에 의해 관리된다.

필터 위치와 구조

필터는 체인으로 구성되며 중간에 필터를 자유롭게 추가 및 제거할 수 있다. 예를 들면 로그를 남기는 필터를 먼저 적용한 후 로그인 여부를 체크하는 필터를 이후에 추가할 수 있다.

필터는 적절하지 않은 요청이 들어올 경우 HTTP요청이 서블릿까지 도달하지 못하게 하는 역할을 한다. 그래서 스프링 시큐리티를 적용할 때 필터에 적용하기도 한다.

ex) 정상적인 요청
HTTP 요청 → WAS → 필터1 → 필터 2 →... → 필터 N → 디스패처 서블릿 → 컨트롤러

ex) 비정상적인 요청
HTTP 요청 → WAS → 필터1 → 필터 2 → 디스패처 서블릿 호출 X

  • 필터 메소드
    • javax.servlet 내에 Filter 인터페이스를 구현해야 함
public interface Filter { // 스프링 빈으로 등록 (스프링 관리할 수 있도록)

    default void init(FilterConfig filterConfig) throws ServletException {}

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

    default void destroy() {}
}
  • init()
    • 필터 객체를 초기화하고 서비스에 추가하기 위한 메소드
    • 웹 컨테이너가 init()메소드를 한번 호출하여 필터 객체를 초기화함
    • 이후 요청들은 doFilter()메소드를 통해 처리됨
  • doFilter()
    • URL 패턴에 맞는 모든 요청이 Dispatcher Servlet으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메소드
    • doFilter의 파라미터로 FilterChain이 있는데 doFilter()를 통해 다음 대상으로 요청을 전달함
    • chain.doFilter() 전후에 우리가 필요한 처리 과정을 넣어줌으로써 원하는 처리 진행 가능함
  • destory()
    • 필터 객체를 서비스에서 제거하고 사용하는 자원을 반환하기 위한 메소드
    • 웹 컨테이너에 의해 한번 호출되며 이후에는 doFilter()메소드를 통해 처리되지 않음

Servlet Filter는 스프링 빈으로 등록이 가능하다?

과거에 필터는 서블릿 기술이라 스프링 빈으로 등록할 수 없었다. 서블릿 기술인 필터는 스프링 컨텍스트 범위 밖인 웹 컨텍스트(서블릿 범위)에서 관리가 되기 때문이다. 하지만 필터에서도 의존성 주입(DI)와 같은 스프링 기술이 필요해지면서 스프링 개발자는 필터도 스프링 빈을 주입받을 수 있도록 대안책을 마련했다. 그것이 바로 DelegatingFilterProxy이다.

 

DelegatingFilterProxy는 서블릿 컨테이너에서 관리되는 프록시용 필터로, 우리가 구현한 필터를 가지고 있다. 우리가 구현한 필터는 스프링 컨테이너 빈으로 등록되는데 요청이 오면 DelegatingFilterProxy가 요청을 받아서 우리가 구현한 필터를 스프링 빈에게 요청을 위임한다.

 

동작원리를 설명하면 다음과 같다.

  1. Filter 구현체가 스프링 빈으로 등록됨
  2. ServletContext가 Filter 구현체를 갖는 DelegatingFilterProxy를 생성함
  3. ServletContext가 DelegatingFilterProxy를 서블릿 컨테이너에 필터로 등록함
  4. 요청이 오면 DelegatingFilterProxy가 직접 처리하지 않고 스프링 빈에게 요청을 위임하여 필터 처리를 진행함

Interceptor

인터셉터는 스프링MVC 제공 기술로, 서블릿 필터 개념을 확장시켜 스프링에서 제공하고 있는 기술이다. 따라서 정확한 이름은 스프링 인터셉터이다.

 

웹 관련 공통 관심 사항을 처리한다는 점에서 필터와 비슷하지만 웹 컨테이너에서 동작하는 필터와 달리 인터셉터는 스프링에서 동작하며 Dispatcher Servlet에 요청이 전달되기 전후가 아닌 Dispatcher Servlet이 컨트롤러를 호출하기 전후에 응답을 참조하거나 가공할 수 있는 기능을 제공한다.

 

그 외에도 서블릿 필터의 경우 단순히 request, response만 제공하지만 스프링 인터셉터는 어떤 컨트롤러가 호출되었는지에 관한 정보도 받을 수 있고 어떤 ModelAndView가 반환되었는지 응답 정보도 받을 수 있는 점에서 필터보다 더 많은 기능을 제공한다고 볼 수 있다.

 

동작 과정을 설명하면 다음과 같다.

  1. Dispatcher Servlet은 핸들러 매핑을 통해 적절한 컨트롤를 찾도록 요청한다.
  2. 결과로 실행 체인(HandlerExcutionChain)을 반환한다.
  3. 해당 실행 체인은 1개 이상 인터셉터가 등록되어 있으면 순차적으로 인터셉터들을 거쳐 컨트롤러를 실행한다.
  4. 인터셉터가 없다면 바로 실행한다.

인터셉터 위치와 구조

스프링 인터셉터 또한 필터와 마찬가지로 체인으로 구성되며 중간에 자유롭게 인터셉트를 추가 및 제거할 수 있다.

스프링 인터셉터의 역할은 적절하지 않은 요청이 들어올 경우 HTTP요청이 서블릿(컨트롤러)까지 도달하지 못하게 하는 것이다.

  • 인터셉터 메소드
    • org.springframework.web.servlet 내의 HandlerInterceptor 인터페이스를 구현해야 함
    • preHandle()
      • 컨트롤러 호출되기 전에 실행됨
      • 컨트롤러 이전에 처리해야하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우 사용함
      • 해당 메소드의 파라미터 중 handler 파라미터는 핸들러 매핑이 찾아준 컨트롤러 빈에 해당하는 HandlerMethod라는 새로운 타입의 객체
        • HandlerMethod - @RequestMapping이 붙은 메소드의 정보를 추상화한 객체
      • preHandle() 반환값은 boolean인데 반환값이 true이면 다음 단계로 진행되지만 false라면 작업을 중단하여 이후 작업이 진행되지 않음
    • postHandle()
      • 컨트롤러를 호출된 후에 실행함
      • 컨트롤러 이후에 처리해야하는 후작업이 있을 경우 사용함
      • 해당 메소드의 파라미터 중 ModelAndView 타입의 정보가 제공됨
        • 최근에는 JSON 형태로 데이터를 제공하는 @RestController를 만들어 자주 사용되지 않음
      • 컨트롤러 하위 계층에서 작업을 진행하다가 예외가 발생하면 해당 메소드는 호출되지 않음
    • afterCompletion()
      • 모든 뷰에서 최종 결과를 생성하는 일을 포함해 모든 작업이 완료된 후에 실행됨
      • 요청 처리 중에 사용한 리소르를 반환할 때 사용하기 적합함
      • postHandle()과 달리 컨트롤러 하위 계층에서 작업 진행하다가 중간에 예외가 발생하더라도 해당 메소드는 반드시 호출됨
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 {
            }
        }

 

 

이전 포스트에서 컨트롤러들에 공통적으로 부가기능을 적용할 때 @ControllerAdvice를 사용하는 방법이 있다고 소개한 적 있다. Interceptor 대신 컨트롤러들에 @ControllerAdvice를 사용하면 Interceptor를 굳이 사용할 필요가 있나? 라는 생각을 할 수 있는데 특정 컨트롤러 호출 과정에 적용되는 부가기능들은 Interceptor를 사용하는 것이 더 낫다.

 

더 나은 이유는 3가지가 있다.

  1. 컨트롤러는 타입과 실행 메소드가 모두 달라 적용할 메소드 작성이 어렵다
  2. 컨트롤러는 파라미터나 반환값이 일정하지 않다.
  3. Interceptor에서는 파라미터로 HttpServletRequest/Response가 넘어온다.

결론적으로 Filter 와 Interceptor를 비교하면 다음과 같이 비교할 수 있을 것이다.

위의 특성 때문에 Filter는 기본적으로 스프링과 무관하게 전역적으로 처리해야하는 작업을 수행하는 예를 들어 이미지나 데이터 압축, 문자열 인코딩과 같이 웹 애플리케이션에 전반적으로 사용되는 기능 구현에 주로 활용하며 Interceptor는 클라이언트 요청과 관련되어 전역적으로 처리해야하는 작업 예를 들면 세부적으로 적용해야하는 인증이나 인가와 같은 작업에 주로 활용된다.

'Spring' 카테고리의 다른 글

Spring - MyBatis  (0) 2022.11.21
Spring - Transaction Manager  (0) 2022.11.21
Spring - JdbcTemplate  (0) 2022.11.21
Spring - ConnectionPool  (0) 2022.11.21
Spring - Log4j2  (0) 2022.11.21

댓글