요청과 응답을 거른 뒤에 정제하는 역할을 합니다.
정제하다 : 불필요한 요소를 제거하고, 명확하고 간결한 형태로 다듬는 과정을 의미.
즉 요청과 응답을 거른 뒤에 내용의 핵심을 추출하고, 중복되거나 불필요한 정보를 제거하여 이해하기 쉽게 만드는 것을 의미합니다.
디스패처 서블릿에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해 부가 작업을 처리할 수 있는 기능을 제공합니다.
디스패처 서블릿은 스프링의 가장 앞단에 존재하는 웹에서 이루어지는 컨트롤러이므로, 필터는 스프링 범위 밖에서 처리가 됩니다.
디스패처 서블릿
클라이언트로부터 어떤 요청이 들어오면 톰캣과 같은 서블릿 컨테이너가 요청을 받는데 이 모든 요청을 디스패처 서블릿이 가장 먼저 받습니다.
그러면 디스패처 서블릿은 공통적인 작업을 우선적으로 함수 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임합니다.
요약 : 서블릿 컨테이너 가장 앞단에서 HTTP 프로토콜로 들어오는 모든 요청을 먼저 받아서 적합한 컨트롤러에 위임해주는 서블릿.

필터를 사용하기 위해서는 Filter 인터페이스를 구현해야 합니다.
public class LogFilter implements Filter {
@Override //필터 초기화 메서드
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");
Filter.super.init(filterConfig);
}
@Override //고객의 요청이 올 때마다 해당 메서드가 호출, 필터의 로직을 구현
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("log filter doFilter");
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();// 모든 사용자의 요청 URI
String uuid = UUID.randomUUID().toString(); //요청 온 것 구분
try {
//로그 남기기
log.info("REQUEST [{}][{}]", uuid, requestURI);
//다음 필터 호출하도록 체인
chain.doFilter(request, response); // 반드시 필요
} catch (Exception e) {
throw e;
} finally {
log.info("RESPONSE [{}][{}]", uuid, requestURI);
}
}
@Override //필터 종료 메서드.
public void destroy() {
log.info("log filter destroy");
Filter.super.destroy();
}
}
ღ init() : 필터 초기화 메서드. 서블릿 컨테이너가 생성될 때 호출된다.
ღ doFilter() : 고객의 요청이 올 때마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.
ღ destroy() : 필터 종료 메서드. 서블릿 컨테이너가 종료될 때 호출된다.
스프링 부트를 사용한다면 FilterRegistrationBean을 사용해서 동록합니다.
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new
FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*"); // 필터를 적용할 URL 패턴을 지정
return filterRegistrationBean;
} }
요청에 대한 작업 전/후로 가로채는 개념입니다.
디스패처 서블릿이 Controller를 호출하기 전/후에 인터셉터가 끼어들어 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공합니다.
웹 컨테이너에서 동작하는 필터와 달리 인터셉터는 스프링 컨텍스트에서 동작합니다.
☪ 스프링 인터셉터는 디스패쳐 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출된다.
☪ 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패쳐 서블릿 이후에 등장하게 된다. 스프링 MVC의 시작점이 디스패쳐 서블릿이라고 생각하면 됨.
☪ 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 URL패턴과는 다르고 정밀하게 설정 가능하다.
서블릿 필터의 경우 단순하게 doFilter() 하나만 제공하지만 인터셉터는 컨트롤러 호출 전(preHandle), 호출 후( postHandle ), 요청 완료 이후( afterCompletion )와 같이 단계적으로 잘 세분화 되어 있다.
서블릿 필터의 경우 단순히 request, response만 제공했지만, 인터셉터는 어떤 컨트롤러가 호출되는지 호출 정보도 받을 수 있습니다. 또한 어떤 ModelAndView가 반환되는지 응답 정보도 받을 수 있습니다.

preHandle : 컨트롤러 호출 전에 호출됩니다. (정확히는 핸들러 어댑터 호출 전에 호출됩니다.)
preHandle 응답값이 true면 다음으로 진행하고 false면 더는 진행하지 않습니다. (false라면 흐름 1번에서 끝.)
postHandle : 컨트롤러 호출 후에 호출됩니다. (정확히는 핸들러 어댑터 호출 후에 호출됩니다.)
afterCompletion : 뷰가 렌더링 된 이후에 호출됩니다.

postHandle: 컨트롤러에 예외가 발생하면 postHandle은 호출되지 않습니다.
afterCompletion은 항상 호출됩니다. (예외를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있습니다.)
LogInterceptor (요청 로그)
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
private static final String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
//모든 사용자들의 요청 URI를 남김.
String uuid = UUID.randomUUID().toString();
//사용자들의 요청 온 것 구분
request.setAttribute(LOG_ID, uuid);
//요청에 데이터 저장 (key, value);
//HandlerMethod
//@RequestMapping과 그 하위 어노테이션(@GetMapping, @PostMapping 등)이 붙은 메소드의 정보를 추상화한 객체
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
//호출할 컨트롤러 메서드의 모든 정보가 포함되어 있다.
}
log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
return true; //true면 다음 컨트롤러 호출
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String uuid = (String) request.getAttribute(LOG_ID);
log.info("RESPONSE [{}][{}][{}]", uuid, requestURI, handler);
//예외가 터진 경우
if (ex != null) {
log.error("afterCompletion error!!", ex);
}
}
}
서블릿 필터의 경우 지역변수로 해결이 가능하지만, 스프링 인터셉터는 호출 시점이 완전히 분리되어있습니다.
preHandle에서 지정한 값을 postHandle, afterCompletion에서 함께 사용하려면 어딘가에 담아두어야 합니다. LogInterceptor도 싱글톤처럼 사용되기 때문에 멤버변수를 사용하면 위험합니다.
그래서 request에 담아두고 이 값은 afterCompletion에서 request.getAttribute(LOG_ID)로 찾아서 사용합니다.
꼭 Filter를 사용해야 하는 경우가 아니라면 인터셉터를 사용하는 것이 더 편리.
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
String requestURI = request.getRequestURI(); log.info("인증 체크 인터셉터 실행 {}", requestURI);
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)
== null) {
log.info("미인증 사용자 요청");
//로그인으로 redirect response.sendRedirect("/login?redirectURL=" + requestURI); return false;
}
return true;
}
}
인증은 컨트롤러 호출 전에만 호출하면 되기 때문에 preHandle만 구현하면 됩니다.
인터셉터를 적용하거나 하지 않을 부분은 addPathPatterns 와 excludePathPatterns 에 작성하면 됩니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error");
registry.addInterceptor(new LoginCheckInterceptor())
.order(2)
.addPathPatterns("/**")
.excludePathPatterns(
"/", "/members/add", "/login", "/logout",
"/css/**", "/*.ico", "/error"
}
}

필터와 인터셉터 모두 비즈니스 로직과 분리되어 특정 요구사항(보안, 인증, 인코딩 등)을 만족시켜야 할 때 적용합니다.
대표적으로 필터를 인증과 인가에 사용하는 도구로는 SpringSecurity가 있습니다.
SprintSecurity는 Spring MVC에 종속적이지 않습니다. (필터 기반으로 인증/인가 처리를 하기 때문)
필터(Filter) 는 특정 요청과 컨트롤러에 관계없이 전역적으로 처리해야 하는 작업이나 웹 어플리케이션에 전반적으로 사용되는 기능을 구현할 때 적용하고,
인터셉터(Interceptor) 는 클라이언트의 요청과 관련된 작업에 대해 추가적인 요구사항을 만족해야 할 때 적용한다.
🖇️ 인터셉터와 필터 차이