김영한님 인강듣고/스프링 MVC

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

doriver 2024. 5. 17. 20:58

필터는 서블릿이 제공하는 기술, 인터셉터는 스프링 MVC가 제공하는 기술이다.

둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위 등이 다르다.

서블릿 필터

필터를 적용하면, 필터가 호출 된 다음에 DispatcherServlet이 호출된다. 
모든 고객의 요청 로그를 남길때, 필터를 사용할수 있다.

 

참고로 필터는 특정 URL 패턴에 적용할 수 있다. ' /* ' 이라고 하면 모든 요청에 필터가 적용된다.

 

필터에서 적절하지 않은 요청이라고 판단하면

DispatcherServlet 을 호출하지 않고, 거기에서 끝을 낼 수도 있다. 
그래서 로그인 여부를 체크하기에 딱 좋다.

 

필터는 체인으로 구성되는데, 중간에 필터를 자유롭게 추가할 수 있다.

로그를 남기는 필터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 필터를 만들 수 있다.

 

 

public interface Filter {  ~  }

필터 인터페이스를 구현하고 등록하면

, 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고 관리한다.

 

public class LogFilter implements Filter {
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException { }
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException { 
        // ServletRequest는 HTTP 요청이 아닌 경우까지 고려해서 만든 인터페이스이다. HTTP를 사용하려면 다운케스팅 하면 된다.
		
		chain.doFilter(request, response); 
        // 다음 필터가 있으면 필터를 호출하고, 필터가 없으면 서블릿을 호출한다. 
        // 만약 이로직을 호출하지 않으면 다음 단계로 진행되지 않는다.
	}
	
	@Override
	public void destroy() { }
}

init() : 필터 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출된다.

doFilter() : 고객의 요청이 올 때 마다 해당 메서드가 호출된다. 여기에 필터의 로직을 구현하면 된다.

destroy() : 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.

 

스프링 인터셉터

스프링 인터셉터는 컨트롤러 호출 직전에 호출 된다. ( DispatcherServlet과 @Controller 사이에서 )

스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 URL 패턴과는 다르고, 매우 정밀하게 설정할 수 있다.

인터셉터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다.( 컨트롤러 호출을 안해버림 )

스프링 인터셉터는 체인으로 구성되는데, 중간에 인터셉터를 자유롭게 추가할 수 있다.
로그를 남기는 인터셉터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다

지금까지 내용을 보면 서블릿 필터와 호출 되는 순서만 다르고, 제공하는 기능은 비슷해 보인다. 
스프링 인터셉터는 서블릿 필터보다 편리하고, 더 정교하고 다양한 기능을 지원한다.

 

스프링의 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다.

public interface HandlerInterceptor { 
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response
    							, Object handler) throws Exception {}
                                
    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() 하나만 제공된다.

인터셉터는 컨트롤러 호출 전( preHandle ), 호 출 후( postHandle ), 요청 완료 이후( afterCompletion )와 같이 단계적으로 잘 세분화 되어 있다.

 

서블릿 필터의 경우 단순히 request , response 만 제공했지만

인터셉터는 어떤 컨트롤러( handler )가 호출되는지 호출정보, 그리고 어떤 modelAndView가 반환되는지 응답 정보도 받을 수 있다.

 

정상 흐름

preHandle : 컨트롤러 호출 전에 호출된다. ( 더 정확히는 핸들러 어댑터 호출 전에 호출된다. )

 

preHandle의 응답값이 true이면 다음으로 진행하고, false이면 더는 진행하지 않는다.

( false인경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝나버림 )

postHandle : 컨트롤러 호출 후에 호출된다. ( 더 정확히는 핸들러 어댑터 호출 후에 호출된다. )
afterCompletion : 뷰가 렌더링 된 이후에 호출된다( 응답 나오고 난 다음에 호출 )

 

컨트롤러에서 예외 발생한 경우

preHandle : 컨트롤러 호출 전에 호출된다.
postHandle : 컨트롤러에서 예외가 발생하면 postHandle은 호출되지 않는다.
afterCompletion : 항상 호출됨. 이 경우 예외( ex )를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력가능

afterCompletion은 예외가 발생해도 호출된다.
예외가 발생하면postHandle() 는 호출되지 않으므로, 예외와 무관하게 공통 처리를 하려면 afterCompletion() 을 사용해야 한다.
예외가 발생하면 afterCompletion()에 예외 정보( ex )를 포함해서 호출된다.