Filter와 OncePerRequestFilter
Filter와 GenericFilterBean
Filter는 javax.servlet-api나 tomcat-embed-core를 사용하면 제공되는 Servlet Filter 인터페이스입니다.
Filter는 서블릿이 실행되기 전, 후로 호출됩니다.
이 Filter 인터페이스를 조금 더 확장하여 스프링에서 제공하는 필터가 바로 GenericFilterBean 입니다.
GenericFilterBean 추상 클래스는 Filter 인터페이스를 상속받고 있는데 이외에도 Spring의 설정 정보를 가져오고 세팅할 수 있도록 확장된 추상 클래스입니다. (ServletContext를 get,set 하는 등..)
Filter 인터페이스 외에도 여러 인터페이스들을 구현하고 있지요?
public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware,
EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean {
}
이 두 필터는 매 서블릿 마다 호출이 된다는 공통점을 가집니다.
서블릿은 사용자의 요청을 받으면 서블릿을 생성해 메모리에 저장해두고, 같은 클라이언트의 요청을 받으면 생성해둔 서블릿 객체를 재활용하여 요청을 처리합니다.
그리고 서블릿은 다른 서블릿으로 dispatch되는 경우가 있습니다.
만약 Servlet Request가 RequestDispatcher 클래스에 의해 다른 서블릿B로 전달(dispatched)된다면,
그 다른 서블릿B가 실행되면서 또 동일한 Filter가 전, 후로 호출이 되는 것입니다.
이렇게 동일한 필터가 반복적으로 호출되는 문제를 해결하한 Filter가 OncePerRequestFilter입니다.
OncePerRequestFilter를 상속받는 필터는 한 요청에 대해 딱 한번만 호출됩니다.
말 그대로 Once. Per. Request "요청 당 한번만" 실행되는 필터라고 해석됩니다.
Filter는 javax.servlet 패키지의 인터페이스인 반면,
OncePerRequestFilter는 GenericFilterBean 추상 클래스를 상속받는 추상 클래스입니다.
※ 서블릿과 구체적인 Filter 동작은 여기에서 설명하였습니다. 참고바랍니다 :^)
대표적으로 Spring Security를 사용할 때 사용될 수 있는데, 예를 들면 인증 작업입니다.
Spring Security에서는 인증과 인가 기능이 Filter로 구현되는데, 이러한 인증과 인가 Filter는
RequestDispatcher 클래스에 의해 다른 서블릿으로 dispatch됩니다.
그러나 인증 작업이 필요한 요청이 들어오면 한 요청에 대해 인증 작업이 딱 한번만 발생하면 됩니다.
그러나 Filter나 GenericFilterBean은 매 서블릿에 도착하기 전에 다시 한번 filter chain을 거치게 됩니다.
이때 한 요청에 딱 한번만 호출되도록 하기위해 OncePerRequestFilter 를 상속받습니다.
OncePerRequestFilter를 사용해보자.
OncePerRequestFilter 추상 클래스를 확장하여 인증 작업을 진행하는 필터를 구현해봅시다.
상속받으면 doFilterIntenal() 를 오버라이드 해야합니다.
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
}
}
Filter 인터페이스에는 아래와 같이 ServletRequest타입으로 넘어오기 때문에 타입 변환을 해주어야 하는 반면,
OncePerRequestFilter 는 타입캐스팅을 할 필요가 없습니다.
// Filter 인터페이스의 메서드
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException;
해당 필터에서 처리해야할 것을 작성한 후에,
마지막에는 해당 필터를 거치고 나서 프로세스가 끝나지 않도록 (이어서 필터 체인을 진행하도록)
인자로 들어온 filterChain의 doFilter()를 호출해주면 됩니다.
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 생략
filterChain.doFilter(request, response);
}
}
참고
https://www.baeldung.com/spring-onceperrequestfilter
https://minkukjo.github.io/framework/2020/12/18/Spring-142/