필터(Filter)
서블릿이나 정적 내용같은 자원 요청이나, 응답에 대해 필터링 작업을 수행하는 객체이다.
필터는 Web Application에 등록하고 요청 스레드가 서블릿 컨테이너에 도착하기 전에 수행되며,
필터는 사용자의 요청 정보에 대한 검증하고 필요에 따라 데이터를 추가하거나 변조할 수 있다.
주로 전역적으로 처리해야하는 인코딩, 보안 관련 일을 수행한다.
사용예시
- 오류 처리 기능
- 인코딩 처리 기능
- 웹 보안 관련 기능 처리
- 데이터 압축이나 변환 기능
- 요청이나 응답에 대한 로그
- 로그인 여부, 권한 검사 같은 인증 기능
필터 구현 예시
@Slf4j
public class TestFilter implements Filter {
private final String value;
public TestFilter(String value) {
this.value = value;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("==========\t" + value + " init filter");
}
@Override
public void destroy() {
log.info("==========\t" + value + " destroy filter");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("==========\t" + value + " before doFilter");
chain.doFilter(request, response);
log.info("==========\t" + value + " after doFilter");
}
}
- Filter 인터페이스를 구현
- WebMvcConfiguration 구현(인터셉터 구간에 기술되어있다.)
- doFilter() 메소드는 필수
- Filter는 doFilter 메소드를 통해 필터링을 한다.
doFilter 메서드는 chain 끝에 있는 리소스에 대한 클라이언트 요청으로 인해
요청/응답 쌍이 chain을 통해 전달될 때 마다 컨테이너에서 호출한다.
- Filter는 doFilter 메소드를 통해 필터링을 한다.
- init(), destroy() 메소드는 디폴트(default) 메소드이므로 필요에 따라 구현한다.
인터셉터(Interceptor)
인터셉터는 스프링 컨텍스트에 등록한다. 서블릿 컨테이너를 통과한 후
컨트롤러에게 요청이 전달되기 전, 후에 대한 처리를 수행한다.
스프링 컨텍스트 내에 존재하기 때문에 모든 Bean객체에 접근할 수 있다.
여러 개의 인터셉터를 사용할 수 있으며 세션처리, 로그인처리, 권한 체크 프로그램 실행시간 계산 등 수행가능한다.
필터와 다르게 handlerMethod 파라미터를 이용해 AOP와 같은 기능 수행이 가능하다.
인터셉터 구현 예시
1. HandlerInterceptor를 구현하는 클래스 작성
@Sl4j
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("==========\tinterceptor preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("==========\tinterceptor postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("==========\tinterceptor afterCompletion");
}
}
2. WebMvcConfiguration 클래스 작성
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new TestFilter(TestFilter.class.getSimpleName()));
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor())
.addPathPatterns("/**");
}
}
/*
- addInterceptors 메소드를 재구현하여 개발자가 직접 구현한 인터셉터를 등록한다.
- 인터셉터가 처리할 path를 지정한다.
*/
필터, 인터셉터의 차이
위의 그림을 볼 때 Filter와 Interceptor의 차이는 크게 아래와 같이 나뉜다.
1. 실행 시점
Filter의 경우 Dispatcher servlet의 앞단에서 정보를 처리하고,
Intercepter는 Dispatcher servlet에서 Handler(controller)에 가기 전에 정보를 처리한다.
2. 제공
Filter의 경우 J2EE 표준스펙에 정의 되어있는 기능이며,
Interceptor의 경우 Spring Framework에서 자체적으로 제공하는 기능이다.
3. 등록
Filter는 Web Application에 등록을 하고, Interceptor는 Spring의 Context에 등록을 한다.
정확히 어떤 상황에 사용해야하는가는 정해져있지않지만
web app 전역적으로 처리해야하는 로직은 Filter로 구현(인코딩 및 보안관련)하고
클라이언트에서 들어오는 디테일한 처리(인증, 권한 등)에 대해서는 주로 Interceptor에서 처리한다.
필터에서만 할 수 있는 일
ServletRequest 혹은 ServletResponse를 교체할 수 있다.
public class SomeFilter implements Filter {
//...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
chain.doFilter(new CustomServletRequest(), new CustomResponse());
}
}
꽤 자주 있는 요구 사항이다.
HttpServletRequest의 body(ServletInputStream의 내용)를 로깅하는 것을 예로 들 수 있을 것 같다.
HttpServletRequest는 body의 내용을 한 번만 읽을 수 있다.
Rest API Application을 작성할 때, 흔히 json 형식으로 요청을 받는다.
@Controller(Handler)에 요청이 들어오면서 body를 한 번 읽게 된다.
때문에 Filter나 Interceptor에서는 body를 읽을 수 없다.
IOException이 발생한다. body를 로깅하기 위해서는 HttpServletRequest를 감싸서
여러 번 inputStream을 열 수 있도록 커스터마이징 된 ServletRequest를 쓸 수밖에 없다.
Interceptor에서만 할 수 있는 일
- AOP 흉내를 낼 수 있다. @RequestMapping 선언으로 요청에 대한 HandlerMethod(@Controller의 메서드)가
정해졌다면, handler라는 이름으로 HandlerMethod가 들어온다.
HandlerMethod로 메서드 시그니처 등 추가적인 정보를 파악해서 로직 실행 여부를 판단할 수 있다.
- View를 렌더링하기 전에 추가 작업을 할 수 있다.
예를 들어 웹 페이지가 권한에 따라 GNB(Global Navigation Bar)이 항목이 다르게 노출되어야 할 때 등의 처리를 하기 좋다.
참고
https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/
https://junhyunny.github.io/spring-boot/filter-interceptor-and-aop/
'스프링' 카테고리의 다른 글
@Transactional (0) | 2022.06.26 |
---|---|
@Controller와 @RestController 차이 (0) | 2022.03.23 |
Domain(Entity) / DTO / DAO (0) | 2022.03.14 |
JPA 연관 관계 정리 / @OneToMany 단방향의 단점 (0) | 2022.03.10 |
Annotation - @Entity (0) | 2022.03.09 |