- Filter 와 Interceptor 모두 Controller (Handler)에 요청이 오기 전과 후에 동작하여 애플리케이션 전반의 공통 관심사를 처리할 수 있다.
- Filter는 Servlet spec, Interceptor는 Spring spec이다.
- Filter는 FilterChain을 통해 필터를 여러개 추가하여 적용할 수 있다. Interceptor도 마찬가지이다.
- 적용 시점은 다음과 같다.
- 요청 → Filter → Dispatcher Servlet → Interceptor → Controller
- Filter가 Interceptor보다 앞단에서 동작한다.
- Interceptor 에서 발생한 예외는 ExceptionHandler로 잡을 수 있지만, Filter에서 발생한 예외는 별도로 처리해주어야한다.
Filter
HTTP 요청 → WAS → 필터 → 서블릿 → 컨트롤러
필터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터 (적절하지 않은 요청이라 판단, 서블릿 호출X) //비 로그인 사용자
필터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다. 그래서 로그인 여부를 체크하기에 딱 좋다.
필터 체인
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러
필터는 체인으로 구성되는데, 중간에 필터를 자유롭게 추가할 수 있다. 예를 들어서 로그를 남기는 필터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 필터를 만들 수 있다.
필터 인터페이스
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(): 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.
Servlet Filter를 스프링 빈으로 등록 가능한 이유
- 과거에는 Filter가 Servlet spec이기 때문에 Spring Bean으로 등록할 수 없었다.
- 하지만, DelegatingFilterProxy가 등장함에 따라 Filter도 Bean으로 등록할 수 있다.
DelegatingFilterProxy
- DelegatingFilterProxy는 서블릿 컨테이너에서 관리되는 프록시용 필터로써 개발자가 생성한 필터를 가지고 있다.
- 개발자가 생성한 필터는 Spring Container에 Bean으로 등록되는데, 요청이 오면 DelegatingFilterProxy가 요청을 받아서 직접 생성한 필터(Spring Bean)에 요청을 위임한다.
- FIlter 구현체가 Bean 으로 등록됨
- ServletContext가 Filter 구현체를 갖는 DelegatingFilterProxy를 생성함
- ServletContext가 DelegatingFilterProxy를 서블릿 컨테이너에 필터로 등록함
- 요청이 오면 DelegatingFilterProxy가 필터 구현체에게 요청을 위임하여 필터 처리를 진행
SpringBoot의 등장 이후
- SpringBoot는 내장 WAS를 사용하기때문에 톰캣과 같은 서블릿 컨테이너까지 SpringBoot가 제어할 수 있다.
- 따라서, SpringBoot가 서블릿 필터를 구현한 Bean을 찾으면 DelegatingFilterProxy 없이 바로 FilterChain에 필터를 등록해주게된다.
- 이로써, Filter를 Spring Bean으로 등록하여 Spring Container의 관리하에 사용할 수 있으면서 Servlet Filter에도 등록할 수 있게된다.
Interceptor
스프링 인터셉터 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> **스프링 인터셉터** -> 컨트롤러
- 스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출 된다.
- 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패처 서블릿 이후에 등장하게 된다. 스프링 MVC의 시작점이 디스패처 서블릿이라고 생각해보면 이해가 될 것이다.
- 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 URL 패턴과는 다르고, 매우 정밀하게 설정할 수 있다
스프링 인터셉터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> **스프링 인터셉터** -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> **스프링 인터셉터**
(적절하지 않은 요청이라 판단, 컨트롤러 호출X) // 비 로그인 사용자
인터셉터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다. 그래서 로그인 여부를 체크하기에 딱 좋다.
스프링 인터셉터 체인
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
- 스프링 인터셉터는 체인으로 구성되는데, 중간에 인터셉터를 자유롭게 추가할 수 있다.
- 예를 들어서 로그를 남기는 인터셉터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다.
스프링 인터셉터 인터페이스
스프링의 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {}
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 {}
}
- 서블릿 필터의 경우 단순하게 doFilter() 하나만 제공된다. 인터셉터는 컨트롤러 호출 전( preHandle ), 호출 후( postHandle ), 요청 완료 이후( afterCompletion )와 같이 단계적으로 잘 세분화 되어 있다.
- 서블릿 필터의 경우 단순히 request , response 만 제공했지만, 인터셉터는 어떤 컨트롤러( handler )가 호출되는지 호출 정보도 받을 수 있다. 그리고 어떤 modelAndView 가 반환되는지 응답 정보도 받을 수 있다.