프로젝트 내 요청/응답에 대한 정보를 로깅하고 관리하기 위해서 서블릿 전후에서 처리하도록 Filter를 구현하는 것으로 결정됐습니다. 이 때, Java Servlet의 기술인 Filter를 Spring 내에서 어떻게 사용하는지에 대해서 알아보기 위해 해당 포스팅을 작성하게 되었습니다.
📜 참고 포스팅
filter는 Java Servlet API의 일부로써, 웹 애플리케이션에서 들어오는 요청과 응답을 가로채어 조작하기 위해 사용됩니다.
핵심적인 메소드 3가지가 존재합니다.
init(FilterConfig filterConfig) : 필터가 생성 시, 초기화 작업을 수행하는 메소드
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) : 요청/응답이 수행될 때 거치는 메소드로, 필터링 로직을 구현합니다.
destroy() : 필터 소멸 시 호출되는 메소드입니다.
Filter는 인터페이스 이므로, 핵심 메소드를 구현해야만 사용이 가능합니다.
Filter를 구현한 예시 코드입니다.
@Component
public class TransactionFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting a transaction for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Committing a transaction for req : {}",
req.getRequestURI());
}
}
GenericFilterBean은 Spring에서 제공하는 추상 클래스입니다. 해당 클래스는 Fitler 인터페이스를 구현하며, Spring의 설정 정보를 가져올 수 있도록 확장된 기능을 제공합니다. 이를 통해서 Spring Context에 접근할 수 있어 보다 유연한 필터링 로직을 구현할 수 있습니다.
하지만, Filter 인터페이스를 직접 구현하더라도 @Component를 통해 빈으로 등록하거나, FilterRegistrationBean\<?> 을 사용하여 Spring Context에 접근이 가능하도록 구현이 가능합니다. 따라서, 개발자의 선호도에 따라 편한 방식으로 구현해주면 됩니다.
public class CustomFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// 전처리
filterChain.doFilter(request , response);
// 후처리
}
}
OncePerRequestFilter는 Spring에서 제공하는 특별한 필터로, HTTP 요청 당 한번만 실행되도록 보장해줍니다. 이는 요청이 다른 서블릿으로 포워딩되더라도 필터가 여러 번 실행되는 것을 방지해줍니다. 즉, 기존의 필터들은 Servlet마다 호출이 됩니다.이 클래스는 doFilterInternal 메소드를 통해서 필터링 로직을 구현합니다.
일반적으로 HTTP 요청마다 한번의 처리 로직을 수행하는 인증처리 로직 구현 시, OncePerRequestFilter를 사용하면 불필요한 인증과정을 한번으로 줄일 수 있습니다.
@Slf4j
public class FirstFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 전처리
filterChain.doFilter(request , response);
// 후처리
}
}
위처럼 Filter를 적용하는 방법에 대해서 차이점을 위주로 간단하게 구현하는 방법에 대해서 설명했습니다. 다음 포스팅에서는 Filter를 실제 프로젝트에 적용할 때, 방식들에 대해서 추가 포스팅 하겠습니다.