1. 필터(Filter)
- 필터(Filter)는 J2EE 표준 스펙 기능으로 디스패처 서블릿(Dispatcher Servlet)에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공한다.
- 디스패처 서블릿은 스프링의 가장 앞단에 존재하는 프론트 컨트롤러이므로, 필터는 스프링 범위밖에서 처리가 된다.
- 즉, 스프링 컨테이너가 아닌 톰캣과 같은 웹 컨테이너에 의해 관리가 되는 것이고(스프링 빈으로 등록은 된다.), 디스패처 서블릿 전/후에 처리하는 것이다.
필터 제한
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() {}
}
- init() : 필터 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출된다.
- doFilter() : 고객의 요청이 올 때 마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.
- destroy() : 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.
2. 인터셉터(Intercepter)
- 스프링이 제공하는 기술로써, 디스패처 서블릿이 컨트롤러를 호출하기 전/후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다.
- 디스패처 서블릿은 핸들러 매핑을 통해 적절한 컨트롤러를 찾도록 요청하는데, 그 결과로 실행 체인(HandlerExecutionChain)을 돌려준다.
- 이 실행 체인은 1개 이상의 인터셉터가 등록되어 있다면 순차적으로 인터셉터들을 거쳐 컨트롤러가 실행되도록 하고, 인터셉터가 없다면 바로 컨트롤러를 실행한다.
스프링 인터셉터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출 X) // 비 로그인 사용자
스프링 인터셉터 체인
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
스프링 인터셉터 인터페이스
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)와 같이 단계적으로 세분화되어있다.
- 인터셉터는 어떤 컨트롤러가 호출되었는지 호출 정보를 받을 수 있다. 또한, 어떤 modelAndView가 반환되는지 응답 정보도 받을 수 있다.
preHandle
- preHandle 메소드는 컨트롤러가 호출되기 전에 실행된다. 그렇기 때문에 컨트롤러 이전에 처리해야 하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우에 사용할 수 있다.
- preHandle의 3번째 파라미터인 handler 파라미터는 핸들러 매핑이 찾아준 컨트롤러 빈에 매핑되는 HandlerMethod라는 새로운 타입의 객체로써, @RequestMapping이 붙은 메소드의 정보를 추상화한 객체이다.
- 또한 preHandle의 반환 타입은 boolean인데 반환값이 true이면 다음 단계로 진행이 되지만, false라면 작업을 중단하여 이후의 작업(다음 인터셉터 또는 컨트롤러)은 진행되지 않는다. (위 그림 1번에서 끝나버린다.)
postHandle
- postHandle 메소드는 컨트롤러를 호출된 후에 실행된다. 그렇기 때문에 컨트롤러 이후에 처리해야 하는 후처리 작업이 있을 때 사용할 수 있다.
- 이 메소드에는 컨트롤러가 반환하는 ModelAndView 타입의 정보가 제공되는데, 최근에는 Json 형태로 데이터를 제공하는 RestAPI 기반의 컨트롤러(@RestController)를 만들면서 자주 사용되지는 않는다.
- 또한 컨트롤러 하위 계층에서 작업을 진행하다가 중간에 예외가 발생하면 postHandle은 호출되지 않는다.
afterCompletion
- afterCompletion 메소드는 이름 그대로 모든 뷰에서 최종 결과를 생성하는 일을 포함해 모든 작업이 완료된 후에 실행된다. 요청 처리 중에 사용한 리소스를 반환할 때 사용하기에 적합하다.
- postHandler과 달리 컨트롤러 하위 계층에서 작업을 진행하다가 중간에 예외가 발생하더라도 afterCompletion은 반드시 호출된다.
- 예외와 무관하게 공통 처리를 하려면 afterCompletion()을 사용해야 한다.
3. 필터와 인터셉터 차이 및 용도
필터와 인터셉터 차이 정리 및 요약
Request/Response 객체 조작 가능 여부
- 필터는 Request와 Response를 조작할 수 있지만 인터셉트는 조작할 수 없다. (내부 상태를 변경한다는 것이 아니라 다른 객체로 바꿔친다는 의미이다.)
public MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 개발자가 다른 request와 response를 넣어줄 수 있음
chain.doFilter(request, response);
}
}
- 필터가 다음 필터를 호출하기 위해서는 필터 체이닝(다음 필터 호출)을 해주어야 한다. 이때 Request/Response 객체를 넘겨주므로 프로그래머가 원하는 Request/Response 객체를 넣어줄 수 있다.
- 하지만 인터셉터는 true나 false에 따라 다음 인터셉트가 실행되거나 컨트롤러로 요청이 전달, 혹은 요청이 중단된다. 그러므로 우리가 다른 Request/Response 객체를 넘겨줄 수 없다.
public class MyInterceptor implements HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Request/Response를 교체할 수 없고 boolean 값만 반환할 수 있다.
return true;
}
}
필터의 용도 및 예시
- 필터에서는 기본적으로 스프링과 무관하게 전역적으로 처리해야하는 작업들을 처리할 수 있다.
- 대표적으로 보안 공통 작업이 있다.
- 또한, 필터는 이미지나 데이터의 압축이나 문자열 인코딩과 같이 웹 애플리케이션에 전반적으로 사용되는 기능을 구현하기에 적당하다.
- 필터를 인증과 인가에 사용하는 도구로는 SpringSecurity가 있다. SpringSecurity의 특징 중 하나는 Spring MVC에 종속적이지 않다는 것인데, 이러한 이유로는 필터 기반으로 인증/인가 처리를 하기 때문이다.
인터셉터 용도 및 예시
- 인터셉터에서는 클라이언트 요청과 관련되어 전역적으로 처리해야 하는 작업들을 처리할 수 있다.
- 대표적으로는 세부적으로 적용해야 하는 인증이나 인가와 같이 클라이언트 요청과 관련된 작업 등이 있다.
- ex) 특정 그룹의 사용자는 어떤 기능을 사용하지 못할 때, 이러한 작업들은 컨트롤러로 넘어가기 전에 검사해야 하므로 인터셉터가 처리하기에 적합하다.
- 인터셉터는 필터와 다르게 Request/Response 객체를 제공받으므로 객체 자체를 조작할 수는 없다. 대신 해당 객체가 내부적으로 갖는 값은 조작할 수 있으므로 컨트롤러로 넘겨주기 위한 정보를 가공하기에 용이하다. 예를 들어 사용자의 ID를 기반으로 조회한 사용자의 정보를 HttpServletRequest에 넣어줄 수 있다.
- 또한 API 호출에 대한 정보들을 기록하기에 용이하다.
출저
인프런 강의 스프링 MVC 2편 (김영한 강사님)
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
'Spring' 카테고리의 다른 글
[Spring] 빈 생명주기 콜백 (0) | 2023.01.30 |
---|---|
[Spring] 조회된 Bean이 두 개 이상일 때 - @Autowired 필드명, @Qualifier, @Primary (0) | 2023.01.30 |
[Spring] @RequestParam, @ModelAttribute, @RequestBody 정리 (0) | 2023.01.29 |
[Spring] MVC 패턴 & 프론트 컨트롤러(Dispatcher-Servlet) (0) | 2023.01.29 |
[Spring] 컴포넌트 스캔과 의존관계 자동 주입(생성자 주입을 해야하는 이유) (0) | 2023.01.29 |