스프링에서 사용하는 filter와 Interceptor에 대해서 알아보자
들어가며
필터(Filter)와 인터셉터(Interceptor)는 모두 웹 애플리케이션에서 클라이언트의 요청과 응답을 가로채어 특정 작업을 수행하는 기능을 제공합니다. 이 두 개에 대해서 한번 알아보도록 하겠습니다.
필터(filter)
필터(filter)란?
서블릿(스프링에서는 DispatcherServlet를 사용)컨테이너 내에서 요청 및 응답을 가로채 조작하는 기능을 제공합니다. 주로 요청 전처리와 응답의 후처리를 수행하며 클라이언트와 서버 간의 통신을 필터링하거나 조작할 수 있습니다.
필터의 동작 방법
- 클라인언트가 요청하면 WAS로 전달
- WAS는 필터로 전달
- 필터는 서블릿으로 전달
- 서블릿은 컨트롤러로 전달
- 컨트롤러는 뷰페이지로 전달한다. 응답은 (요청의 역순으로 진행 5>4>3>2>1)
필터가 존재한다면 필터가 중간에 가로채 해당 로직을 진행 후에 서블릿으로 전달한다.
Spring에서의 Filter인터페이스
java.Servlet의 Filter인터페이스를 상속받아서 사용하면 된다
Filter.interface
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {}
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;{}
default void destroy() {}
}
- default vooid init(FilterConfig filterConfig) : 필터가 초기화될 때 호출되는 메서드. 처음생성될 때 한번 호출된다.
- void doFilter(...) : 필터가 요청을 처리하는 메서드이다. 파라미터로는 request, response, chain이 넘어온다.
- default void destroy() : 필터가 소멸될 때 사용되는 메서드로 필터가 소멸되면 이 메서드라 실행된다.
defailt가 작성되어 있는 메서드는 인터페이스로 구현할 때 꼭 오버라이드를 안 해도 되는 메서드이다.
Spring에서의 필터를 활용환 로그출력
모든 컨트롤러에서 로그를 출력하는 필터를 한번 만들어보자
LogFilter
@Slf4j
public class Logfilter implements Filter {
@Override
public void init(FilterConfig filterConfig){
log.info("필터시작");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest)request;
String requestURI = servletRequest.getRequestURI();
String uuid = UUID.randomUUID().toString();
try{
log.info("요청 [{}] [{}]",requestURI,uuid);
chain.doFilter(request,response);
}catch (Exception e){
e.printStackTrace();
}finally {
log.info("응답 [{}] [{}]",uuid,requestURI);
}
}
@Override
public void destroy() {
log.info("필터 소멸");
}
}
클라리언트가 요청을 하면 로그를 남겨주는 필터이다. 요청을 하면 요청한 uuid와 요청한 URI를 남겨주고 응답은 요청했던 uuid와 URI를 넘겨준다.
chain.doFilter(request, reponse)를 꼭 작성해 주자 작성하지 않으면 동작하지 않는다.
이제 작성한 필터를 사용하기 위해서는 스프링 컨테이너에 올려야 한다.
webConfig
@Configuration
public class webConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean logFilter(){
FilterRegistrationBean<Filter> filter = new FilterRegistrationBean<>();
filter.setFilter(new Logfilter()); //LogFilter을 사용
filter.setOrder(1); // 순서를 첫번째로 시작한다.
filter.addUrlPatterns("/*"); // 적용할 url 모든 url에 적용한다.
return filter;
}
}
이제 실행해 보자
처음으로 애플리케이션을 시작하면
init() 메서드가 시작되면서 "필터시작" 로그가 시작되게 된다.
만든 페이지를 접속하면 doFilter이 실행되면서 요청과 응답을 로그를 남기며,
애플리케이션을 중지하게 되면 destory() 메서드가 실행되면서 "필터 소멸"로그를 남기게 된다.
인터셉터(Interceptor)
인터셉터란?
인터셉터는 스프링 MVC 프레임워크에서 컨트롤러(Controller)에 요청이 도달하기 전과 후에 실행되는 기능을 제공합니다. 이것은 일반적으로 요청 전처리 또는 요청 후처리 작업을 수행하는 데 사용됩니다.
인터셉터란의 동작방법
- 클라이언트가 요청을 보내면 WAS로 도착
- 필터가 요청을 가로채간다
- 가로챈 요청을 처리하고 Servlet에 보낸다.
- 디스패처 서블릿은 인터셉터를 호출하여 요청을 전처리 작업을 실행한다.
- 컨트롤러에서 비니지스 로직을 싱행한다.
- 뷰로 전송한다. (응답은 요청의 역순으로 진행)
스프링 MVC의 컨트롤러와 관련된 작업을 처리하기 위한 것입니다.
HandlerInterceptor인터페이스 이해
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
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을 구현받아 사용해야 한다. HandlerInterceptor의 메서드를 알아보자
- preHandle(...) : 컨트롤러(Controller)의 실행 이전에 호출되는 메서드이며. boolean을 사용해 true일 때만 핸들러가 실행된다.
- postHandle(...) : 컨트롤러의 실행 후에 호출되는 메서드이며, 뷰가 렌더링 되기 전에 추가적인 작업을 사용가능하다.
- afterCompletion(...) : 요청 처리가 완료된 후에 호출되는 메서드임, 뷰가 렌더링 된 후에 추가적인 작업이 가능하다.
인터셉터를 사용한 로그 남기기
LogInterceptor
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
private static final String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();
request.setAttribute(LOG_ID,uuid);
//핸들러 정보 출력
if (handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
}
log.info("요청 [{}] [{}] [{}] [{}]",uuid,requestURI, request.getDispatcherType(),handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("컨트롤러 실행후 발생! [{}]",handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String logId = (String) request.getAttribute(LOG_ID);
log.info("응답 [{}] [{}] [{}] [{}]",logId,requestURI,request.getDispatcherType(),handler);
if (ex!=null){
log.error("에러발생 error!!",ex);
}
}
}
- request.getDispatcherType() : 현재 어떤 요청으로 들어오는지 확인하기 위해서 작성
- REQUEST: 직접적인 클라이언트 요청으로부터 발생한 디스패치입니다. 일반적으로 사용자가 웹 브라우저를 통해 서버에 직접 요청한 경우에 해당합니다.
- FORWARD: 서블릿 내에서 RequestDispatcher.forward()메서드를 호출하여 다른 서블릿이나 JSP로 요청을 전달한 경우에 해당합니다.
- INCLUDE: 서블릿 내에서 RequestDispatcher.include() 메소드를 호출하여 다른 서블릿이나 JSP의 실행 결과를 포함한 요청을 처리한 경우에 해당합니다.
- ASYNC: 비동기적인 요청을 처리하는 경우에 해당합니다. 서블릿 3.0부터 추가된 기능으로, 비동기적인 요청을 처리할 때 사용됩니다.
이 코드는 단순한 로그를 출력하기 위한 코드이므로 단순하게 작성하지만 , 부가적인 기능을 작성하고 싶다면 추가해서 사용하면 됩니다.
작성한 인터셉터를 사용하기 위해서는 필터와 마찬가지로 스프링컨테이너에 올려야 한다.
WebConfig
@Configuration
public class webConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()) //로그 인터셉터사용
.order(1) //첫번째로 실행
.addPathPatterns("/**") //모든패턴에대해서 적용
.excludePathPatterns("/css/**","/*.ico","/error","error-page/**"); //해당 url은 무시한다.
}
}
스프링 컨테이너에 InterceptroRegistry를 사용해 등록해 준다.
이제 애플리케이션을 실행해 로그를 보면 원하는 대로 나오는 것을 볼 수이다. getDispatcherType()은 요청한 값이기 때문에 request라고 출력되는 것을 볼 수 있다.
필터와 인터셉터의 차이
지금까지 필터와 인터셉터의 대해서 간단하게 예시를 통해서 알아봤다. 하지만 필터와 인터셉터 모두 웹애플리케이션 요청을 가로채 처리하는 기능을 제공한다 해서 아무거나 사용해도 되는 거 아닌가? 생각이 들 수 있다. 하지만 다음과 같이 차이점이 존재한다.
- 필터 : 서블릿의 기술이므로 서블릿에 스펙에 하며 서블릿 컨테이너 생명주기 내에서 처리를 하게 된다.
- 인터셉터 :스프링의 기술이므로, IoC컨테이너에서 관리된다. 또한 스프링빈으로 등록된 MVC의 핸들러 실행 전후에 사용된다.
그래서 둘 중에 필터와 인터셉터를 선택하는 것이 좋을까? 내 생각은 개발환경과 기능적 요구사항에 따라 선택하면 될 것 같다.
'Spring > spring' 카테고리의 다른 글
[Spring] 스프링의 객체지향 원리 간단하게 이해하기 (0) | 2024.02.28 |
---|---|
[Spring] "객체지향 디자인 패턴 이해하기: 템플릿 메서드, 전략, 콜백" (1) | 2024.02.07 |
[스프링] AOP(Aspect-Oriented-Programming)이해 (0) | 2023.11.22 |
[스프링] 트랜잭션(Transaction)이란 (0) | 2023.11.22 |
스프링 서비스 추상화 (1) | 2023.11.19 |