본문 바로가기

개발중/Spring

서블릿 필터와, 스프링 인터셉터 ( 면접단골질문 ) 🎹

728x90
반응형

 


공통 관심사

대부분 많은 웹 서비스는 로그인을 해야 서비스를 이용할 수 있다.

로그인을 하지 않은 사용자는 접근할 수 있는 페이지가 제한적이며 로그인이 필요한 페이지 접근이 허용되서는 안된다. 

하지만, 그렇다고 로그인이 필요한 모든 컨트롤러 로직에 로그인 여부를 확인하는 코드를 작성하는 것은 너무 비효율적이다. 

수정에도 취약하다.

이렇게 많은 로직에서 공통으로 관심이 있는 부분을 공통 관심사(cross-cutting concerns)라 한다.
여러 로직에서 공통으로 로그인에 관심을 가지고 있는데, 이러한 공통 관심사는 스프링에서 AOP로 처리할 수 있다. 

 

하지만, 웹에 관련된 공통 관심사는 스프링 AOP 보다는 서블릿 필터, 스프링 인터셉터에서 처리하는게 좋다. 

웹과 관련된 공통 관심사를 처리할 때는 HTTP의 헤더나 URL 정보가 필요한데 서블릿 필터나, 스프링 인터셉터는 HttpServletRequest를 제공하기 때문이다.


서블릿 필터 vs 스프링 인터셉터

서블릿 필터와 스프링 인터셉터는 모두 웹과 관련된 공통 관심사를 처리하는데 사용되는 기술이다.
필터는 서블릿에서 제공하고 인터셉터는 스프링 MVC가 제공하는 기술인데, 적용되는 순서와 범위, 그리고 사용방법이 다르다.


필터와 인터셉터의 흐름


필터를 적용하면 필터가 호출된 이후 서블릿이 호출된다.

(여기서 서블릿은 스프링의 경우 디스패처 서블릿을 의미한다고 생각하면 된다.)


인터셉터를 적용하면 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출된다.
필터는 서블릿 호출전에 인터셉터는 서블릿 호출 이후 호출되기에 인터셉터는 서블릿에서 예외가 발생한다면 호출되지 않는다.

 

필터와 인터셉터의 제한

필터와 인터셉터는 각각 요청이 적절하지 않을경우 자신의 상태에서 종료할 수 있다.

필터는 서블릿까지도 가지 못하지만, 스프링 인터셉터는 서블릿까진 통과 후 제한이 된다.

 

필터와 인터셉터 체인

 

둘 다 자유롭게 필터 및 인터셉터를 추가할 수 있다.
로그를 남기는 필터(혹은 인터셉터)를 적용 후 그 다음 로그인 여부를 체크하는 필터(혹은 인터셉터)를 만들어 적용할 수 있다.

여기까지 보면 호출시점이 서블릿 호출 전/후의 차이를 빼면 별 차이가 없어보인다. 

하지만, 스프링 인터셉터는 좀 더 편하고 정교하며 다양한 기능을 제공한다.


필터 인터페이스

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, destroy 메서드는 default 메서드 이기에 따로 구현하지 않아도 된다.
  • init()
    • 필터 초기화 메서드로 서블릿 컨테이너가 생성될 때 호출된다.
  • doFilter()
    • 고객의 요청이 올 때마다 해당 메서드가 호출된다. 
  • destroy()
    필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.

 

인터셉터 인터페이스

public interface HandlerInterceptor {
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}

 

doFilter 하나로 로직을 수행하는 서블릿 필터와는 다르게 인터셉터는 다음과 같이 3가지 단계로 세분화 되어있다.

 

  • 컨트롤러 호출 전(preHandler)
  • 컨트롤러 호출 후(postHandle)
  • 요청 완료 이후(afterCompletion)


서블릿 필터는 단순히 서블릿 호출 전 doFilter 하나만 호출되어 사용되는데, 

스프링 인터셉터는 컨트롤러의 호출전, 호출 후, 요청 완료 이후 3가지나 세분화되어 호출된다. 

그럼 이 세가지는 그냥 차례대로 호출될리는 없는데, 이러한 세 메서드의 호출은 다음과 같은 흐름으로 이뤄진다. 

 

 

 

  • (1)
    • preHandler: 컨트롤러 호출 전에 호출되며 반환 타입은 Boolean 이다. 즉, 반환 값이 false이면 그 뒤는 진행하지 않는다.
  • (4)
    • postHandler: 컨트롤러 호출 후 호출되며 정확히는 핸들러 어댑터 호출 후 호출된다. 
  • (6)
    • afterCompletion: 뷰가 렌더링 된 후에 호출된다. 

 

  • preHandle
    • 컨트롤러 호출 전에 호출된다. 
  • postHandler
    • 컨트롤러에서 예외가 발생하면 postHandler은 호출되지 않는다.
  • afterCompletion
    • afterCompletion은 항상 호출된다. 
    • (try-catch의 finally처럼) 그렇기에 이전에 발생한 예외가 있을 경우 이를 파라미터로 받아서 어떤 예외가 발생했는지 확인할 수 있다.
    • 즉, 예외가 발생하면 postHandler()같은 경우 호출되지 않기 때문에 예외 처리가 필요하다면 afterCompletion()을 사용해야 한다. 
    • 다행히 afterCompletion은 Exception ex를 매개변수로 받고 있으며 Nullable하기에 notNull인 경우 해당 처리를 수행하면 된다. 

 

 

728x90
반응형