필터

Lilac-_-P·2023년 4월 18일
0

스프링 MVC

목록 보기
10/15

웹 애플리케이션을 개발하다보면, 애플리케이션의 여러 로직에서 공통적으로 관심을 갖게 되는 요소가 있다. (ex. 인증, 로깅, 전처리 등)

이러한 공통 관심사는 스프링의 AOP(내부적으로 프록시 패턴 사용)나 디자인 패턴으로도 해결할 수 있지만, 웹과 관련된 공통 관심사는 서블릿 필터나 스프링 인터셉터를 사용하는 것이 좋다. 웹과 관련된 공통 관심사를 처리할 때는 HTTP의 헤더나 URL의 정보들이 필요한데, 서블릿 필터나 스프링 인터셉터는 HttpServlet을 제공하기 때문이다.

서블릿 필터 흐름

스프링 MVC에서 서블릿 필터의 흐름은 다음과 같다.

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

필터를 적용하면 필터가 호출된 다음에 서블릿이 호출된다. 그래서 특정 서블릿이 호출되기전에 처리해야하는 공통 관심사가 존재한다면, 필터에서 처리하면 된다. 필터는 특정 URL 패턴에 선택적으로 적용할 수 있기 때문에, WAS로 들어오는 특정 URL로의 요청중 원하는 요청만 골라서 공통 관심사 처리를 수행할 수 있다.

참고.
스프링 MVC에서는 프론트 컨트롤러 패턴 도입으로 DispatcherServlet 하나만 Servlet으로 등록되기 때문에,
위의 서블릿 필터 흐름에서의 서블릿은 DispatcherServlet 으로 생각하면 된다.

즉, 필터를 이용하면 아래와 같은 제한을 둘 수 있다.

[적절한 요청] : HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

[부적절한 요청] : HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출 X) -> 서블릿 -> 컨트롤러

또한, 필터는 체인으로 구성되기 때문에 중간에 필터를 자유롭게 추가할 수 있다. 처리해야하는 공통 관심사가 여러개라면, 순서를 잘 고려하여 필터를 적용하면된다.

HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러

필터는 서블릿에서 제공하는 Filter 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고, 관리한다. 아래의 코드를 보자.

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() {}
}

인터페이스의 각 함수는 다음을 뜻한다.

  • init() : 필터 초기화 메서드로 서블릿 컨테이너가 생성될 때 호출된다.
  • doFilter() : HTTP 요청이 WAS로 올 때 마다 이 메서드가 호출된다. 필터의 로직을 여기에 구현하면 된다.
  • destroy() : 필터 종료 메서드로 서블릿 컨테이너가 종료될 때 호출된다.

여기서 doFilter() 가 가장 중요한 함수라는 것은 누가봐도 알 것이다. 해당 함수를 좀 더 자세히 알아보자.

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
            
	// 필터에서 구현하고자하는 핵심 로직
    
	chain.doFilter(request, response) // 매우 중요!
    
    // return; // 매우 중요!
}

위의 코드에서 중요한 부분은 2가지이다.
주석으로도 적어놓았지만, chain.doFilter()와 return; 이 매우 중요하다. 하나씩 알아보자

위에서 필터는 체인으로 구성되기 때문에 중간에 필터를 자유롭게 추가할 수 있다고 했었다.함수 내에 적혀있는 chain.doFilter() 가 바로 다음 필터를 호출하는 함수이다. chain.doFilter()를 호출했을 때 다음 필터가 있으면 필터를 호출하고, 필터가 없다면 서블릿을 호출한다. 만약 필터내에서 이 함수의 호출이 없다면, 다음단계로 진행할 수 없다.

또, 필터는 부적절한 요청에 대해서 적절하지 않은 요청이라 판단되면 서블릿 자체를 호출하지 않을 수도 있었다. 필터 내부의 로직에서 요청이 적절하지 않다고 판단되면, return문을 통해 함수를 종료시켜버리면, 필터는 return문 이후의 로직을 수행하지 않는다. 당연히 다음 필터, 서블릿도 호출되지 않는다. 만약 함수를 종료시키는 경우에는, doFilter() 함수의 파라미터로 주어지는 ServletResponse를 적절히 이용하여 응답이 어떻게 반환될 것인지 처리해주면 된다.

참고.
필터는 서블릿에 종속적인 기술이기 때문에, 스프링이 제공하는 기술은 아니다. 하지만, 스프링 부트에서는 필터를 손쉽게 등록하는 방법을 제공한다. @Configuration을 이용한 설정 파일에서 FilterRegistrationBean 클래스를 스프링 빈으로 등록하면 된다. 자세한 것은 필요할 때 찾아볼 것.

서블릿 필터를 적절하게 사용하면, 서블릿이 호출되기 전에 HTTP 요청을 걸러낼(필터링)할 수 있다.
요 근래에 스프링 시큐리티를 이용한 로그인 기능을 개발하고 있는데, 스프링 시큐리티가 필터를 사용하여 구현되어있다. 그만큼 어떻게 동작하는지 이해하고 사용하면 강력한 기술이다.

profile
열심히 하자

0개의 댓글