필터, 인터셉터 - Filter

김회민·2023년 2월 21일
0

Spring

목록 보기
16/25

Filter

HTTP 요청 → WAS(톰캣) → 필터 → 서블릿 (디스페처 서블릿) → 컨트롤러

필터란, 클라이언트의 요청을 중간에 낚아채서 로직을 처리하기 위해 제공되는 기능이다.

필터는 여러 체인으로 구성되며, 프로그래머가 정한 순서에 맞게 해당 로직을 처리할 수 있도록 한다.

doFilter 메서드를 통해 원하는 로직을 구현하면 되고, 만약 필터를 진행하다 로직이 실패하면 다음 순서로 진행되지 않고 바로 WAS로 올라가게 된다.

Filter 인터페이스

package jakarta.servlet;

public interface Filter {
    /**
     * 필터 초기화 메서드
     * 서블릿 컨테이너가 생성될 때 호출된다.
     */
    public default void init(
            FilterConfig filterConfig
    ) throws ServletException {}

    /**
     * 고객의 요청이 올 때 마다 해당 메서드가 호출된다.
     * 필터의 로직을 구현하면 된다.
     */
    public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain
    ) throws IOException, ServletException;

    /**
     * 필터 종료 메서드
     * 서블릿 컨테이너가 종료될 때 호출된다.
     */
    public default void destroy() {}
}

필터 인터페이스를 상속받아서 구현하면 되며, 구현체를 등록하면 IoC 컨테이너는 이를 싱글톤 Bean으로 생성하고 관리한다.

  • void init()
    • 필터가 초기화된 후 호출되는 메서드이다.
    • 서블릿 컨테이너가 생성될 때 호출된다.
    • FilterConfig를 이용해 어떤 식으로 설정되었는지 확인할 수 있다.
  • void doFilter()
    • 클라이언트에서 요청을 하면 서블릿에 가기 전에 해당 메서드를 호출한다.
    • 해당 로직이 완료되어 다음 순서로 넘어가기 위해선 해당 메서드안에서 chain.doFilter를 호출해주어야 한다.
    • ServletRequest는 HttpServletRequest의 부모 클래스인데, Http에 관련한 메서드가 존재하지 않기 때문에, 이를 사용하기 위해선 타입을 변환해주어야 한다.
  • void destory()
    • 필터가 삭제되기 전 호출되는 메서드이다.
    • 서블릿 컨테이너가 종료될 때 호출된다.

구현해보기 - LogFilter

@Slf4j
public class LogFilter implements Filter {
    @Override
    public void init(
            FilterConfig filterConfig
    ) throws ServletException {
        log.info("log filter init");
    }

    @Override
    public void destroy() {
        log.info("log filter destroy");
    }

    @Override
    public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain
    ) throws IOException, ServletException {
        log.info("log filter doFilter");

        HttpServletRequest req = (HttpServletRequest) request;
        String requestURI = req.getRequestURI();

        String uuid = UUID.randomUUID().toString();

        try {
            log.info("REQUEST [{}][{}]", uuid, requestURI);
            chain.doFilter(request, response);
        } catch (Exception e) {
            log.error("Filter Error: [{}]", (Object) e.getStackTrace());
            throw e;
        } finally {
            log.info("RESPONSE [{}][{}]", uuid, requestURI);
        }
    }
}

WAS → 필터 1 → 필터 2 → 서블릿 → 컨트롤러 → 서블릿 → 필터 2 → 필터 1 → WAS

필터는 서블릿이 호출되기전에 호출되며, doFilter 내부에서 다음 chain의 doFilter 메서드를 호출하는 방식으로 진행되므로 위과 같은 흐름으로 진행된다.

chain.doFilter(request, response); 를 생략하게 되면 다음 순서로 진행되지 않으며, 함수는 종료되고 WAS로 올라가게 된다.

필터 등록

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<Filter> logFilter() {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new LogFilter());
        bean.setOrder(1);
        bean.addUrlPatterns("/*");

        return bean;
    }
}

위와 같이 @Configuration 애노테이션이 붙은 클래스에 Spring Bean으로 등록하면 된다.

  • public void setFilter(T *filter*)
    • 필터의 구현체를 등록하는 메서드이다.
  • public void setOrder(int *order*)
    • 필터의 순서를 지정하는 메서드이다.
    • 낮을 수록 먼저 동작한다.
  • public void addUrlPatterns(String... *urlPatterns*)
  • public void setUrlPatterns(Collection<String> *urlPatterns*)
    • 필터를 적용할 URL을 지정하는 메서드이다.
    • whitelist 방식이기 때문에 원하는 URL에 한해 지정할 수 있다.
profile
백엔드 개발자 지망생

0개의 댓글