[Spring] 서블릿 Filter와 스프링 Interceptor의 개념과 사용방법
1. Filter와 Interceptor 개념
- Filter와 Interceptor은 공통적인 관심 사항이 있을 때 사용합니다.
- 공통적인 관심 사항 : 여러 로직에서 공통으로 관심있는 관심사
- Filter와 Interceptor는 HttpServletRequest를 제공합니다.
- 예) 로그인을 안하면 회원 수정, 삭제, 게시물 삭제, 등록 등을 보지를 못합니다. 여기서 공통으로 로그인에 대한 인증을 관심을 가지고 있습니다. 이 관심사를 Filter와 Interceptor로 해결할 수 있습니다.
2. 서블릿 Filter
- 필터의 흐름
적절한 요청이 왔을때 : Http 요청 -> 서버 -> 필터 -> 서블릿 -> 컨트롤러
적절하지 않은 요청이 왔을 때 : Http요청 -> 서버 -> 필터 -> X
- 적절한 요청이 왔을때는 컨트롤러를 호출하는데 적절한 요청이 오지 않았을 경우 필터에서 서블릿을 호출하지 않습니다.
- 필터 체인
필터의 흐름에서 필터를 자유롭게 추가할 수 있습니다.
- 필터 체인 예시 : Http 요청 -> 서버 -> 필터 -> 필터 -> 필터 -> 서블릿 -> 컨트롤러
필터 구현
// Filter
public FilterEx implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String uuid = UUID.randomUUID().toString();
try{
log.info("REQUEST [{}][{}]", uuid, requestURI);
chain.doFilter(request,response);
}catch (Exception e) {
throw e;
}finally {
log.info("RESPONSE [{}][{}]", uuid, requestURI);
}
}
@Override
public void destroy() {
}
}
- 필터를 사용할려면 Filter 인터페이스를 구현해야 합니다.
- init : 필터 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출됩니다.
- destroy : 필터 종료 메서드 , 서블릿 컨테이너가 종료될 때 호출됩니다.
- doFilter : 요청이 왔을 때 호출된다.
- HttpServletRequest httpRequest = (HttpServletRequest) request : ServletRequest는 Http 요청이 아닌 것까지 고려해서 만든 것이라 HttpServletRequest로 다운 캐스팅을 해준다.
- chain.doFilter(request, response) : 다음 필터가 있으면 다음 필터를 호출하고 없으면 서블릿을 호출한다.
* 참고 : init과 destroy 메서드는 구현 안 해줘도 됩니다. 왜냐하면 Filter에서 default로 메서드를 정의해 놓았기 때문입니다.
필터 등록
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new FilterEx()); // 필터 등록
filterFilterRegistrationBean.setOrder(1); // 순서 => 숫자가 작을수로 우선순위 높음
filterFilterRegistrationBean.addUrlPatterns("/*"); // /* 모든 경로
return filterFilterRegistrationBean;
}
}
- 스프링 부트를 사용할 경우 FilterRegistrationBean를 사용해서 필터를 등록해주면 됩니다.
- setFilter(new FilterEx()) : 구현한 필터를 등록합니다.
- setOrder(1) : 필터 순서를 설정해줍니다.(숫자가 낮을수록 우선순위가 높습니다.)
- addUrlPatterns("/*") : 필터를 적용할 경로를 설정해줍니다.
3. 스프링 Interceptor
- Interceptor 흐름
적절한 요청이 왔을 때 : Http 요청 -> 서버 -> 필터 -> 서블릿 -> Interceptor -> 컨트롤러
적절하지 않은 요청이 왔을때 : Http 요청 -> 서버 -> 필터 -> 서블릿 -> Interceptor -> x
- 적절한 요청이 오면 Interceptor에서 컨트롤러를 호출하는데 적절하지 않은 요청은 컨트롤러를 호출하지 않는다.
- Interceptor 체인
필터와 마찬가지로 Interceptor를 중간에 자유롭게 추가할 수 있다.
- Http 요청 -> 서버 -> 필터 -> 서블릿 -> Interceptor -> Interceptor -> Interceptor -> 컨트롤러
- Interceptor 호출 흐름
- preHandle() 호출
- handle 반환
- ModelAndView 반환
- postHandle() 호출
- render 호출
- afterCompletion() 호출
Interceptor 구현
public class InterceptorEx implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();
request.setAttribute("logId", uuid);
if(handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
}
log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String logId = (String)request.getAttribute("logId");
log.info("RESPONSE [{}][{}]", logId, requestURI);
if(ex !=null) {
log.error("afterCompletion error!!", ex);
}
}
}
- HandlerInterceptor 인터페이스를 구현해야 합니다.
- preHandle : 컨트롤러 호출 전에 호출됩니다.
- preHandle return 이 false 이면 preHandle 호출하는 부분에서 끝나고 true이면 컨트롤러를 호출합니다.
- postHandle : 컨트롤러 호출 후에 호출됩니다.
- 예외가 발생하면 postHandle를 호출되지 않는다.
- afterCompletion : 뷰 랜더링 후에 호출됩니다.
- 예외가 발생해도 afterCompletion은 호출됩니다.
Interceptor 등록
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new InterceptorEx())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**","/*.ico","/error");
}
}
- addInterceptor(new InterceptorEx()) : 구현한 Interceptor를 등록합니다.
- order(int order) : Interceptor의 우선순위를 설정합니다.( 숫자가 낮을수록 우선순위가 높습니다.)
- addPathPatterns("/**") : Interceptor를 적용할 경로를 지정합니다.(/** => 전체 경로)
- excludePathPatterns("/css/**", "/*. ico", "/error") : Interceptor에서 제외할 패턴을 지정합니다.
정리
인터셉터는 스프링 MVC 구조에 특화된 필터 기능을 제공합니다. 스프링 MVC를 사용하고, 특별히 필터를 사용해야 하는 상황이 아니라면 인터셉터를 사용하는 것이 더 편리합니다.
'Spring Boot' 카테고리의 다른 글
[Spring Boot] JUnit5에서 @RequiredArgsConstructor (0) | 2022.04.22 |
---|---|
[Spring] 예외처리와 오류 페이지 (0) | 2022.01.04 |
[Spring] @ScriptAssert 오류내용 및 해결방법 (0) | 2021.12.28 |
[Spring] 메시지, 국제화 개념 및 사용 방법 (0) | 2021.12.26 |
[Spring]@RestController, @Controller 개념 및 예시 (0) | 2021.12.19 |
댓글
이 글 공유하기
다른 글
-
[Spring Boot] JUnit5에서 @RequiredArgsConstructor
[Spring Boot] JUnit5에서 @RequiredArgsConstructor
2022.04.22 -
[Spring] 예외처리와 오류 페이지
[Spring] 예외처리와 오류 페이지
2022.01.04 -
[Spring] @ScriptAssert 오류내용 및 해결방법
[Spring] @ScriptAssert 오류내용 및 해결방법
2021.12.28 -
[Spring] 메시지, 국제화 개념 및 사용 방법
[Spring] 메시지, 국제화 개념 및 사용 방법
2021.12.26