공통 관심사 (cross-cutting concern)
- 웹 개발시 공통적으로 처리해야 할 업무들이 많다.
- 로그인 관련 (세션체크) 처리, 권한 체크
- 로그
- 페이지 인코딩 변환
- XSS 방어
- 공통업무에 관련된 코드를 모든 페이지 마다 작성된다면 중복된 코드의 증가하고 유지보수도 하기 힘들어진다.
- 공통 관심사는 따로 관리하는것이 좋다.

Filter
- 서블릿에서 지원해주는 기술.
Filter 흐름
HTTP 요청 → WAS → 필터 → 서블릿(디스패처 서블릿) → 컨트롤러
필터를 적용하면 필터가 호출 된 다음에 서블릿이 호출된다.
모든 고객의 요청 로그를 남기는 요구사항이 있다면 필터를 사용하면된다.
(”/*” 설정시 모든 요청에 필터가 적용된다)
필터 제한
HTTP 요청 → WAS → 필터 → 서블릿 → 컨트롤러 // 로그인 사용자
HTTP 요청 → WAS → 필터 (적절하지 않은 요청이라 판단, 서블릿 호출 X) // 비 로그인 사용자
- 필터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다. 그래서 로그인 여부를 체크하기에 좋다.
필터 체인
HTTP 요청 → WAS → 필터1 → 필터2 → 필터3 → 서블릿 → 컨트롤러
필터는 체인으로 구성되는데, 중간에 필터를 자유롭게 추가할 수 있다.
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() {} }
Filter 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 Filter를 싱글톤 객체로 생성하고, 관리한다.
- init()
- Filter 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출된다.
- doFilter()
- 고객의 요청이 올 때 마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.
- destroy()
- Filter 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.
2. Interceptor (인터셉터)
- 서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술
둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위, 그리고 사용방법이 다르다.
Interceptor 흐름
HTTP 요청 → WAS → 필터 → 서블릿(Dispatcher Servlet) → 스프링 인터셉터 → 컨트롤러

- 스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출된다.
- 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 URL 패턴과는 다르고, 매우 정밀하게 설정할 수 있다.
Interceptor 제한
HTTP 요청 → WAS → 필터 → 서블릿 → 스프링 인터셉터 → 컨트롤러 // 로그인 사용자
HTTP 요청 → WAS → 필터 → 서블릿 → 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출X) // 비 로그인 사용자
인터셉터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다.
로그인 여부를 체크하기에 딱 좋다.
스프링 인터셉터 체인
HTTP 요청 → WAS → 필터 → 서블릿 → 인터셉터1 → 인터셉터2 → 컨트롤러
- 스프링 인터셉터는 체인으로 구성되는데, 중간에 인터셉터를 자유롭게 추가할 수 있다.
- 예를 들어서 로그를 남기는 인터셉터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다.
Interceptor 인터페이스
public interface HandlerInterceptor { // Controller 요청 전 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } // Controller 요청 후 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } // HTTP 요청이 완전히 끝날 때 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은 호출되지 않는다.
- afterCompetion : afterCompletion은 항상 호출된다. 이 경우 예외(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.
afterCompletion은 예외가 발생해도 호출된다.
- 예외가 발생하면 postHandle()은 호출되지 않으므로 예외와 무관하게 공통 처리를 하려면 afterCompletion()을 사용해야 한다.
필터와 인터셉터의 용도 및 예시
필터
- 공통된 보안 및 인증/인가 관련 작업
- 모든 요청에 대한 로깅 또는 검사
- 이미지/데이터 압출 및 문자열 인코딩
- Spring과 분리되어야 하는 기능
스프링과 무관하게 전역적으로 처리해야 하는 작업들을 처리할 수 있다. 보안 공통 작업 ⇒ 전역적으로 해야하는 보안 검사 (XSS 방어)를 하여 올바른 요청이 아닐 경우 차단을 할 수 있다. 스프링 컨테이너까지 요청이 전달되지 못하고 차단되므로 안정성을 더욱 높일 수 있다.
인터셉터
- 세부적인 보안 및 인증/인가 공통 작업
- API 호출에 대한 로깅 또는 검사
- Controller로 넘겨주는 정보 (데이터)의 가공
클라이언트의 요청과 관련되어 전역적으로 처리해야 하는 작업들을 처리할 수 있다.
정리
- 인터셉터는 스프링 MVC 구조에 특화된 기능을 제공한다고 이해하면 된다. 스프링 MVC를 사용하고, 특별히 필터를 꼭 사용해야 하는 상황이 아니라면 인터셉터를 사용하는 것이 더 편리하다.
- 인터셉터를 적용하거나 하지 않을 부분을 패턴을 사용해서 세밀하게 설정할 수 있다.
⇒ 이러한 이유로 인터셉터에서 많이 인증, 인가 코드를 구현하였다.
- 대표적으로 필터를 인증과 인가에 사용하는 도구 : Spring Security
- Spring MVC에 종속적이지 않다.
- (스프링과 관련 없이 독립적으로 활동하던 프로젝트)
- Filter, Interceptor에서 하는 것 그 중 어떤 것이 안전하냐 무의미
- 체크 위치와 방법의 차이 ⇒ 고려해서 결정