필터는 서블릿이 제공하는 기능이고, 인터셉터는 스프링이 제공하는 기능이다.
클라이언트 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 -> 핸들러(컨트롤러)
이미지 출처 - https://mangkyu.tistory.com/173
인터셉터는 디스패처 서블릿에서 핸들러 어댑터를 통해 핸들러를 호출하기 전에 호출이 된다.
하지만 필터는 클라이언트의 요청이 들어와서 WAS를 통해 서블릿으로 가기 전에 호출된다.
package hello.login.web.test;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("필터-init: 필터 초기화 메서드 - 서블릿 컨테이너가 생성될 때 호출");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 필터 기능을 여기에 구현하면 된다.
log.info("필터-doFilter: 클라이언트 요청 시 호출");
/*
HttpServletRequest httpRequest = (HttpServletRequest) request; // 캐스팅
if(httpRequest.getSession(false) == null) {
log.info("필터-doFilter: 세션이 없는 클라이언트");
return;
}
*/
chain.doFilter(request, response);
log.info("필터-doFilter: 컨트롤러 로직 이후 호출");
}
@Override
public void destroy() {
log.info("필터-destroy: 필터 종료 메서드 - 서블릿 컨테이너 종료 시 호출");
}
}
필터에서 사용하는 request는 HTTP가 아닌 여러 요청을 받기위해 ServletRequest
를 사용한다.
그러므로 HTTP 기능을 처리하려면 HttpServletRequest
로 다운 캐스팅해준다.
스프링 부트에서 필터를 등록할 때는 FilterRegistrationBean
를 빈으로 등록하고 해당 빈에 만든 Filter를 설정해주면 된다.
package hello.login.web;
import hello.login.web.filter.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean filter1() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.addUrlPatterns("/*"); // 필터 적용 Url
filterRegistrationBean.setOrder(1); // 우선 순위
filterRegistrationBean.setFilter(new LogFilter()); // 필터 등록
return filterRegistrationBean;
}
// 다른 필터
@Bean
public FilterRegistrationBean filter2() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
...
return filterRegistrationBean;
}
}
만약 여러 필터를 등록할 때는 여러 Bean을 등록하면 된다.
package hello.login.web.test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("인터셉터-preHandle: 컨트롤러 호출 전에 호출(정확히는 핸들러 어댑터 호출 전에 호출)");
/*
// 실제 인터셉터 로직
if(request.getSession(false) == null) {
log.info("인터셉터: 세선이 없는 사용자");
// false를 반환하면 여기서 여기서 끝남
return false;
}
*/
// true를 반환하면 진행하며 컨트롤러까지 진행
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("인터셉터-postHandle: 컨트롤러 호출 후에 호출(정확히는 핸들러 어댑터 호출 후 호출)");
// exception이 핸들러에서 발생해서 던져진다면 호출되지 않음
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("인터셉터-afterCompletion: 뷰가 렌더링 된 이후 호출");
// exception이 핸들러에서 발생해서 던져진다해도 호출된다.
// ex로 던져진 예외를 볼 수 있다.
}
}
WebMvcConfigurer
에 addInterceptors
를 구현하고 register
에 인터셉터를 등록하면 된다.
package hello.login.web;
import hello.login.web.test.LogFilter;
import hello.login.web.test.LogInterceptor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.Filter;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/members/add") // 이 부분이 필터와 차별화된 기능
.order(1);
}
...
}
excludePathPatterns
와 같이 인터셉터를 적용하지 않을 부분은 따로 빼낼 수 있다. 이 부분은 필터와 차별화된 기능이다.
이외에도 인터셉터는 추가적인 기능을 제공한다.
로그를 확인하기 위해 다음과 같은 컨트롤러를 추가했다.
로그를 확인해보자.
인터셉터는 필터에 비해 훨씬 다양한 기능을 제공한다.
필터보다는 인터셉터를 사용하는 것이 훨씬 좋다.
특별한 경우가 아니라면 인터셉터를 사용하는 것이 좋다.