Filter와 Interceptor 더불어 AOP까지 모두 공통 관심사를 처리하는데 사용하는 기능들입니다.
Filter 와 Interceptor는 웹 적인 요소(URL,Protocol 등)를, AOP는 비즈니스적 측면(Method,Annotation 등)을 다룬다는 점에서 차이가 있습니다.
이렇게 각자 실행되는 시점이나 할 수 있는 것들에 차이가 있기 때문에 요구사항에 맞춰 알맞은 기능을 사용하면 됩니다.
다만 요새는 Filter보다는 interceptor 가 많이 쓰이는 추세라고 합니다.
Filter
- 스프링 외부의 서블릿에서 제공하는 공통 처리 기능
- 흐름의 가장 앞에서 요청과 응답을 거른 뒤 정제하는 역할을 합니다.
- 요청이 DispatcherServlet에 도착하기 전에 스프링 컨텍스트 외부에서 요청을 필터링합니다.
- 사용자의 요청 정보에 대한 검증, 데이터 추가 혹은 변조, 자원의 처리 후 응답 정보에 대한 변경 등의 처리가 가능합니다.
- 주로 전역적으로 처리해야하는 로깅이나 인코딩 변환, XSS 방어 등 웹 보안 관련 기능, 인증 기능 등을 수행합니다.
- 예외처리는 try-catch로 처리합니다.
Filter 흐름
// 필터 로그인 인증 예시
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러 // 로그인 사용자(일반 흐름)
HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출X) // 비 로그인 사용자
// 필터 체인
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러
- 필터를 적용하면 필터가 호출된 다음에 서블릿(Dispatcher Servlet)이 호출됩니다.
- 필터는 특정 URL 패턴에 적용할 수 있습니다.
- 필터에서 적절하지 않은 요청이라고 판단되면 서블릿을 호출하지 않고 끝냅니다.
- 필터를 체인으로 사용할 수 있습니다.
Filter 생성 방법
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 인터페이스를 구현하여 생성합니다.
- init()
- 필터 인스턴스를 초기화 합니다.
- doFilter()
- 공통 관심사를 처리할 로직을 작성합니다.
- HTTP 요청이 오면 doFitler가 호출 됩니다.
- ServletRequest 는 HTTP요청이 아닌 경우까지 고려해서 만든 인터페이스이기 때문에 HTTP를 사용하려면 HttpServletRequest로 다운캐스팅 해서 사용하면 됩니다.
- ServletRequest를 커스터마이징 해서 다음 필터로 건내줄 수 있습니다.
- FilterChain으로 다음 필터가 존재하면 호출하고 없으면 서블릿을 호출합니다.
- chain.doFilter(request,response); 를 호출하지 않으면 다음 단계로 진행되지 않습니다.
- destroy()
- 필터 인스턴스를 종료합니다.
- init()
Filter 등록 방법
- 설정파일에 FilterRegistrationBean을 빈 등록하고 필터 설정
- 필터를 등록할 때마다 새로 빈을 등록해야합니다.
- 이 방식을 사용하는게 제일 무난합니다.
- setFilter()
- 등록할 필터를 지정
- setOrder()
- 필터 체인의 적용 순서를 지정, 숫자가 낮을 수록 먼저 적용됩니다.
- addUrlPatterns() , setUrlPatterns()
- 필터를 적용할 URL 패턴을 지정합니다.
- 한번에 여러 패턴을 지정할 수 있으며 전자는 문자열 가변인수로,후자는 list로 받습니다.
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
- @WebFilter + @ServletComponentScan
- 필터 구현 클래스에 @Webfilter(urlPatterns = “적용할 URL패턴”)
- 설정 파일에 @ServletComponentScan
- 이 방식은 순서 조절이 안되기 때문에 사용에 주의해야한다.
- 필터에 @Component 추가
- 이 방식으로 하면 컴포넌트 스캔할 때 필터가 등록된다.
- @Order 로 필터 간의 순서를 정해줄 수 있다.
- 다만, 이 방식은 url패턴을 지정할 수 없기 때문에 기본적으로 모든 url 패턴에 매핑된다.
Interceptor
- 스프링 MVC가 제공하는 공통 처리 기능
- 스프링의 DistpatcherServlet이 컨트롤러를 호출하기 전, 후로 끼어들기 때문에 스프링 컨텍스트 내부에서 Controller(Handler)에 관한 요청과 응답에 대해 처리합니다.
- 구체적인 시점에 구체적인 동작이 가능합니다. (pre,post,after)
- 실제 매핑된 Handler 정보를 확인 가능합니다.
- 스프링 레벨에서 지원하기 때문에, 스프링의 모든 빈 객체에 접근할 수 있습니다.
- 여러 개를 사용할 수 있습니다.
- 스프링 URL 패턴을 적용할 수 있는데 서블릿 URL 패턴과는 다르게 매우 정밀하게 설정할 수 있습니다.
- 스프링 PathPattern
PathPattern (Spring Framework 5.3.20 API)? 한 문자 일치 * 경로(/) 안에서 0개 이상의 문자 일치 ** 경로 끝까지 0개 이상의 경로(/) 일치 {spring} 경로(/)와 일치하고 spring이라는 변수로 캡처 {spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring" {spring:[a-z]+} regexp [a-z]+ 와 일치하고, "spring" 경로 변수로 캡처 {*spring} 경로가 끝날 때 까지 0개 이상의 경로(/)와 일치하고 spring이라는 변수로 캡처 /pages/t?st.html — matches /pages/test.html, /pages/tXst.html but not /pages/ toast.html /resources/*.png — matches all .png files in the resources directory /resources/** — matches all files underneath the /resources/ path, including / resources/image.png and /resources/css/spring.css /resources/{*path} — matches all files underneath the /resources/ path and captures their relative path in a variable named "path"; /resources/image.png will match with "path" → "/image.png", and /resources/css/spring.css will match with "path" → "/css/spring.css" /resources/{filename:\\\\w+}.dat will match /resources/spring.dat and assign the value "spring" to the filename variable
- 인증/인가 등과 같은 공통 작업, API 호출에 대한 로깅 ,Controller로 넘겨 주는 데이터 가공 등에 사용됩니다.
Interceptor 흐름
필터와 거의 동일합니다.
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출 X) // 비 로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
Interceptor 생성방법
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 {}
}
- HandlerInterceptor를 구현하여 생성합니다.
- 각 메소드에서 request나 response 객체에 setAttribute등을 통해 값을 담아 넘겨줄 수 있습니다.
- preHandle에서 지정한 값을 request.setAttribute를 통해 값을 저장해놓으면 postHandle, afterCompletion에서 getAttribute를 통해 사용할 수 있습니다.
- preHandle()
- 컨트롤러가 호출되기 전에 실행될 로직을 작성합니다.
- 3번째 파라미터인 handler는 @RequestMapping(축약형 포함)이 붙은 메소드의 정보를 추상화한 객체입니다.
- 반환값은 boolean으로, 반환값이 true 이면 다음 단계로 진행, false이면 작업을 중단하고 다음단계(다음 인터셉터 혹은 컨트롤러)로 넘어가지 않습니다.
- postHandle()
- 컨트롤러가 호출된 후에 실행될 로직을 작성합니다.
- 정확히는 핸들러 어댑터 호출 후에 호출됩니다.
- view가 있다면 view 페이지 렌더링 되기 전에 호출됩니다.
- Handler 뿐만 아니라 어떤 ModelAndView가 반환되는지 응답 정보도 받을 수 있습니다.
- 컨트롤러에서 예외 발생 시, postHandel()은 호출되지 않습니다.
- 컨트롤러가 호출된 후에 실행될 로직을 작성합니다.
- afterCompletion()
- 뷰가 렌더링 된 이후에 호출됩니다.
- 모든 작업이 완료된 이후에 실행된다고 보면 됩니다.
- 컨트롤러에서 예외가 발생하더라도 항상 호출됩니다.
- 이 경우, 4번째 파라미터인 ex를 통해 어떤 예외가 발생했는지 알 수 있습니다.
- 예외와 무관하게 공통처리를 하고자한다면 afterCompletion()을 사용해야 합니다.
- 뷰가 렌더링 된 이후에 호출됩니다.
- 각 메소드에서 request나 response 객체에 setAttribute등을 통해 값을 담아 넘겨줄 수 있습니다.
Interceptor 등록방법
- WebMvcConfigurer를 구현한 설정 파일에 addInterceptors() 오버라이딩
- registry.addInterceptor()
- 인터셉터를 등록합니다.
- order()
- 인터셉터 순서를 지정합니다. 낮을 수록 먼저 호출됩니다.
- addPathPatterns()
- 인터셉터를 적용할 URL 패턴을 지정합니다.
- excludePathPatterns()
- 인터셉터에서 제외할 패턴을 지정합니다.
- registry.addInterceptor()
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error");
}
//...
}
'Spring Framework > Spring' 카테고리의 다른 글
롬복 (Lombok) (0) | 2022.06.09 |
---|---|
스프링 MVC - 예외처리 (REST API) (0) | 2022.06.09 |
스프링 MVC - HTTP Request,Response (0) | 2022.06.08 |
스프링 MVC - 전체 구조 (0) | 2022.06.08 |
Spring Validation (0) | 2022.06.07 |
댓글