스프링을 배우면서 유저의 로그인을 판단하는 로직을 짜려고 했을 때 내가 처음 떠올렸던 것은 바로 필터 이다. 필터로 걸러내면 되지 않겠나 했는데, 알고 보니 인터셉터 라는 것도 있다는 것을 알게 되었다.
뭔가 비슷해 보이는데 둘의 차이가 뭔지 탐구해보도록 하자
필터는 Client 의 요청이 디스패처 서블릿(Dispatcher Servlet) 에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대한 부가 작업을 처리할 수 있는 기능을 제공한다.
즉, 요청이 Spring 으로 가기 전인 Spring 밖에서 일처리가 진행된다는 것이다. 이를 그림으로 표현할 사진이 있다.

이를 자바에서 구현해놓은 클래스가 존재한다.
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()
필터 객체를 초기화 하고 서비스에 추가하기 위한 메소드, 웹 컨테이너가 1회 init()을 호출하여 필터 객체를 초기화한 이후 요청들은 doFileter()를 통해 처리된다.
doFilter()
url-pattern에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메소드이다.
doFilter의 파라미터로 FilterChain 이 있는데, FilterChain.doFilter() 를 통해 다음 Filter로 연결시켜줄 수 있다.
destroy()
필터 객체를 제거하고 사용하는 자원을 반환하기 위한 메소드, 웹 컨테이너가 1회 destroy() 를 호출하여 필터 객체를 종료하면 이후에는 doFilter에 의해 처리되지 않는다.
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에 등록이 가능하다.
인터셉터는 Spring이 제공하는 기술로써, 디스패처 서블릿(Dispatcher Servlet) 이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다.
즉 웹 컨테이너에서 동작하는 필터와는 달리 인터셉터는 스프링 컨텍스트에서 동작한다.
디스패처 서블릿은 Client 에게 요청을 받은 후 이 요청을 처리할 알맞은 컨트롤러를 찾아주는데 이를 찾아주기 전에 인터셉터가 작동하는 것 이다.
이를 그림으로 표현하면 아래와 같다.

이를 스프링에서 구현해놓은 클래스는 다음과 같다.
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 메소드
컨트롤러가 호출되기 전에 실행되는 메소드, 컨트롤러 이전에 처리해야 하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우 사용 가능하다. preHandle 의 반환 타입은 boolean 인 걸 알 수 있는데 true 이면 다음 단계로 진행이 되지만, false 라면 작업을 중단하여 다음 작업을 진행하지 않는다.
postHandle 메소드
컨트롤러가 호출된 후에 실행되는 메소드, 컨트롤러 이후에 처리해야하는 후처리 작업이 있을 때 사용 가능하다. 컨트롤러 이후에 호출되어 컨트롤러 하위 계층에서 작업하다 예외 처리가 될 시 이 메소드는 실행되지 않는다.
afterCompletion 메소드
모든 작업이 완료된 후에 실행되는 메소드, 요청 처리 중에 사용한 리소스를 반환할 때 사용하기에 적합하다. postHandle 메소드와는 달리 중간에 예외가 발생하더라도 반드시 호출된다.
필터와 인터셉터의 가장 큰 차이점이라고도 볼 수 있다. 서로 관리되는 컨테이너가 다른데 필터는 스프링 이전의 서블릿 영역에서 관리되지만, 인터셉터는 스프링 영역에서 관리된다. 이로 인해 필터는 스프링이 처리해주는 내용들을 적용받을 수 없다. 예를 들어 스프링에 의한 예외처리가 불가능하다.
참고로 관리되는 컨테이너가 필터는 스프링 밖이지만 빈으로는 등록이 가능하다. 뭔가 이상하다 생각되는데 이것이 가능한 이유는 다음에 다뤄보도록 하자.
필터는 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를 넘겨주는 코드가 없으므로 객체 수정이 불가능하다.
| 대상 | 필터 | 인터셉터 |
|---|---|---|
| 관리되는 컨테이너 | 서블릿 컨테이너 | 스프링 컨테이너 |
| Request/Response 객체 수정 가능 여부 | 가능 | 불가능 |
| 용도 | 1. 공통된 보안 및 인증/인가 관련 작업 2. 모든 요청에 대한 로깅 또는 감사 3. 이미지/데이터 압축 및 문자열 인코딩 4. Spring과 분리되어야 하는 기능 | 1. 세부적인 보안 및 인증/ 인가 공통 작업 2. API 호출에 대한 로깅 또는 감사 3. Controller 로 넘겨주는 정보의 가공 |