Spring 에서 각 엔드포인트 또는 특정 엔드포인트 요청 마다 공통된 작업을 수행하기 위해 사용되는 대표적인 방식으로
Filter와 Interceptor가 있다.
Filter 는 웹 컨테이너에 존재하며 DispatcherServlet 이전에 동작한다.
Interceptor 는 DispatcherServlet 이후에 동작하며 Spring 컨텍스트에 접근 할 수 있다.
이 두 Filter 와 Interceptor 의 활용 방법을 알아보자.

Filter 는 Spring Context 밖에 위치하며 웹 컨텍스트에 존재한다.
요청이 DispatcherServlet 에 전달되기 이전에 수행되며 Spring Context 에는 접근하지 못한다.
Filter 는 다음과 같은 인터페이스를 가지고 있다.
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
init() 서블릿 컨테이너에 의해 Filter가 처음 생성 될 때 호출된다.Filter가 초기화 될 때 필요한 리소스나 설정을 로드하는데 사용되며, 주로 데이터베이스 연결 초기화, 서블릿 컨텍스트 접근, 로그 초기화 등과 같은 설정 작업을 수행한다.FilterConfig를 통해 Filter의 정보나 서블릿 컨텍스트에 접근할 수 있다.FilterConfig
public interface FilterConfig {
String getFilterName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
FilterChain 기반으로 동작하며 정의된 필터 체인을 따라 다음 필터로 요청을 전달한다.@Component
@Order(1)
public class MyFilter implements Filter {
// Filter 메서드 구현
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> loggingFilter() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MyFilter());
registrationBean.setOrder(1);
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
}
Interceptor는 Spring context 내부에 위치하며 요청이 Dispatcher Servlet 에서 Controller 로 전달되기 이전에 수행된다.
또한 Controller로 요청이 전달 된 이후에 사용자에게 응답이 전달 될 때에도 영향을 줄 수 있다.
Spring Context 내부에 위치하여 빈 객체들에 접근이 가능하다.

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 {
}
}
ModelAndView 를 인자로 가지고 있으며 이를 통해 뷰에 대한 로직을 수행 가능postHandle()은 수행되지 않지만, afterCompletion()은 항상 수행된다.Java Servlet API 에서 제공되는 기능으로, Spring 과 같은 FrameWork와 독립적으로 활용될 수 있다.CORS Filter 예시
public class CORSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*"); // 허용하는 도메인 설정
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
preHandle, postHandle, afterCompletion 세 단계로 나누어 요청의 처리 전후 과정을 세분화하여 제어할 수 있다.Filter 는 Framework에 의존하지 않고 수행되어야 하는 기능들을 정의하기에 좋고, interceptor 보단 상대적으로 이른 타이밍에 로직이 수행된다.
요청의 이른 시점에 수행할 로직이나, 서버 엔드포인트에 도달하기 이전에 수행될 로직을 정의하기에 좋다.
Interceptor의 가장 큰 장점은 빈 객체에 접근할 수 있다는 점이다. 따라서 비즈니스 로직과 관련된 기능을 활용할 수 있으며, 요청에 대한 비즈니스 로직과 관련된 처리가 필요하다면 Interceptor가 적합해 보인다.
특별한 상황이 아니라면 Interceptor 를 활용하는 것이 더 편리하여 이를 주로 이용하는 편이다.
특정 조건에 따라 Filter 를 적용하지 않을 요청을 선별하려 한다면, 주로 ServletRequest 에서 얻을 수 있는 요청의 메타 데이터 만을 이용하게 된다.
보통은 요청 경로, 헤더 등을 통해 선별을 하는 경우가 많다.
하지만 이는 새로운 경로가 추가되거나, 관련 헤더가 변경될 경우 현행화가 잘 되지 않거나, 놓치기 쉬운 부분이다.
따라서 Controller 메서드에 애노테이션을 사용하여 특정 요청에만 Filter가 적용되게 구현하려 하였지만, Filter 는 Controller에 요청이 도달하기 훨씬 이른 시점에 수행되며 구현이 불가능 했다.
반면 Interceptor 는 DispatcherServlet이 요청을 수행할 handler(Controller)를 결정한 이후에 수행되며, preHandle() 메서드의 handler 파라미터를 통해 Controller 메서드의 정보를 가져올 수 있다.
Reflection을 통해 Controller 메서드에 적용된 애노테이션 정보를 가져올 수 있고, 애노테이션 방식으로 Interceptor 적용 여부를 핸들링 할 수 있다.