Framework/Spring

[Spring] 서블릿 필터와 스프링 인터셉터 비교하기!

leegeonwoo 2024. 8. 7. 03:22

일반적인 웹 페이지는 로그인한 사용자와 로그인하지 않은 사용자를 구분하여 역할에 맞는 기능을 제공해야한다.

먼저, 프론트 화면처리로 로그인하지 않은 사용자에게는 상품관리라는 버튼을 숨겨서 기능을 제공하지 않을 수 있지만, URL경로를 직접 호출하게 된다면 프론트 처리만으로는 서비스에 제한을 둘 수 없다.
이런 로그인 여부확인은 회원가입, 정보수정, 삭제 등등 많은 서비스에서 사용되는데 여러 곳에서 공통으로 사용되는 로직을 웹에서는 스프링인터셉터를 사용하여 웹과 관련된 공통 관심사를 처리할 수 있다.

서블릿 필터

  • 필터는 서블릿 스펙의 일부로 스프링 MVC와는 독립적으로 동작하며 모든 요청에 대해 동작할 수 있다.
  • javax.servlet.Filter인터페이스를 사용한다.
  • 모든 HTTP요청에 대해 공통적으로 처리해야 할 로직이 있을 때 사용(인코딩설정, CORS, 로깅 등)
  • 스프링 MVC이외의 서블릿이나 JSP와 같은 다른 기술과 함께 사용될 때 사용
  • 서블릿 컨테이너 레벨에서 요청을 가로채고 처리해야할 때 사용

==서블릿이 지원하는 필터==
필터의 정상흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

필터의 제한흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 호출X

예를들어 필터에서 로그인되지않은 사용자라고 판단하면 서블릿을 호출하지않고 필터에서 옳바르지않은 요청에대한 응답을 처리할 수 있다.

==스프링 부트에서 필터 등록==
스프링에서 제공하는 FilterRegistrationBean을 사용하면 스프링 빈으로 필터를 편리하게 등록할 수 있다.

  • setFilter(new ExampleFilter()): 등록할 필터를 지정
  • setOrder(1): 필터가 여러개 있을 때 동작 순서를 지정한다.
  • addUrlPatterns("/*"): 필터를 적용할 URL패턴을 지정한다.

참고로 @ServletComponentScan, @WebFilter와 같은 어노테이션으로 필터를 등록할 수 있지만 이 경우에는 필터 순서 조절이 불가능하기때문에 FilterRegistrationBean을 사용하는 것을 권장한다.

==예제==

@Configuration  
public class FilterConfiguration {  

    @Bean  
    public FilterRegistrationBean<MyFilter> testFilter() {  
        FilterRegistrationBean<MyFilter> filter = new FilterRegistrationBean<>();  
        filter.setFilter(new MyFilter());  
        filter.addUrlPatterns("/test");  

        return filter;  
    }  
}

FilterRegistrationBean을 통하여 필터를 등록한다.

public class MyFilter implements Filter {  

    private int number = 0;  

    @Override  
    public void init(FilterConfig filterConfig) throws ServletException {  
        number++;  
        System.out.println("필터 시작 " + number);  
    }  

    @Override  
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {  
        number++;  

        HttpServletRequest request = (HttpServletRequest) servletRequest;  
        String requestURI = request.getRequestURI();  
        HttpServletResponse response = (HttpServletResponse) servletResponse;  
        response.setStatus(205);  

        System.out.println("요청 URI: " + requestURI);  
        System.out.println("doFilter " + number);  

        filterChain.doFilter(request, servletResponse);  
    }  

    @Override  
    public void destroy() {  
        System.out.println("필터 종료 " + number);  
    }  
}

아무의미없는 필터를 구현한 클래스로 필터에 사용 시점마다 number변수에 +1을 해주며 doFilter에서 요청에 대한 URL을 출력하고 다음 필터 또는 컨트롤러를 호출하도록 한다.

필터 시작 1
...SPRING BOOT 실행로그 생략...
요청 URI: /test
doFilter 2
컨트롤러 1

스프링 부트애플리케이션이 올라가기전에 필터가 먼저 시작되고, 컨트롤러를 호출하기전에 필터가 사용되는 것을 실행결과를 통해 확인할 수 있다.

스프링 인터셉터

스프링 인터셉터도 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결하는 기술이다.
인터셉터는 스프링 MVC가 제공하는 기술로 몇 가지 차이점이있다.

==스프링 인터셉터 흐름==
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러

==스프링 인터셉터 제한 흐름==
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 호출 X

스프링 인터셉터는 서블릿 필터보다 편리하고 더 정교한 다양한 기능을 지원한다.
인터셉터를 사용하기위해서는 HanlderInterceptor를 구현하면 된다.

  • preHandle()
  • postHandle()
  • afterCompletion()
    서블릿 필터의 경우에는 doFilter()메서드 하나만 제공되기때문에 컨트롤러가 호출되기 전에 사용되는 필터만 가능했지만,
    인터셉터를 사용한다면 컨트롤러 호출 전, 호출 후, 요청완료 이후와 같이 세분화된 상태로 사용할 수 있다.

==스프링 인터셉터 예외==

  • preHandle은 컨트롤러 호출 전에 호출되기때문에 예외와 관계없다.
  • postHandle은 컨트롤러에서 예외가 발생하면 호출되지 않는다.
  • afterCompletion은 항상 호출되기때문에 어떤 예외가 발생됐는지 확인할 수 있다.

==스프링 인터셉터 등록==
WebMvcConfigurer를 구현한 설정 클래스를 통해 인터셉터를 편리하게 등록할 수 있다.

  • InterceptorRegistry: 인터셉터를 등록하는 클래스
  • addInterceptor(new ExampleInterceptor()): 인터셉터를 등록
  • addPathPatterns("/**): 인터셉터를 적용할 URL패턴을 지정한다.
  • excludePathPatterns(): 인터셉터에서 제외할 패턴을 지정한다.

참고
PathPattern에대한 공식문서
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/ web/util/pattern/PathPattern.html

728x90