Spring Framework/SPRING MVC

Spring Intercept

거북이의 기술블로그 2024. 10. 7. 14:47
spring mvc Intercept...

 

Intercept 란?

Servlet 필터와 유사한 역할을 하는 기능.
다만, 차이점이 있다면 Spring MVC 에서 제공하는 기능이며, Servlet Filter 이후에 동작하는 차이점이 존재
  • Intercept의 특징은 Servlet Filter에서 제공하는 doFilter() 메서드 한개와 다르게 순서에 따라 3개의 메서드가 존재

 

Intercept 흐름

HTTP 요청 -> WAS -> Filter -> Servlet -> Intercept -> Controller
  • Servlet Filter가 Intercept 보다 먼저 호출됨
  • 중복 처리 되지 않도록 주의하며 설계해야함

 

Intercept 인터페이스

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 {
    }
}
  • preHandler() : Controller 호출 전에 호출 ( boolean 반환값으로, true 이면 다음으로 진행되고 false 이면 다음으로 넘어가지 않음 )
  • postHandler() : Controller 호출 후에 호출 ( Controller에서 예외가 발생하면 postHandler() 메서드는 호출되지 않음 ) 
  • afterCompletion() : view 렌더링 이후 호출 ( Controller에서 예외가 발생하더라도 afterCompletion() 메서드는 호출됨 )
    • afterCompletion() 메서드의 경우 Controller에서 예외 발생시 예외 정보를 반환할 수 있음

 

Intercept 구현

  • 주의할점
    • MyInterceptor 클래스의 경우 bean에 등록되므로, 싱글톤으로 진행되게 된다 (멤버변수를 주의하여 사용해야함)
      • uuid를 활용하여 request에 요청별로 구분하고, 가져다 사용할 수 있도록 진행
      • 이렇게 불편하게(?) 사용한 이유는 , preHandler() / postHandle() / afterCompletion() 호출 시기가 다 다르기 때문에 이런식으로 구현하게 됨
    • Controller 핸들러 종류
      • @RequestMapping (동적 처리)
        • HandlerMethod인 경우, 추가적인 정보가 존재하기에 필터링하여 사용할 수 있다. 
        •  
      • 정적리소스 처리 (/resources/static)
        • ResourceHttpRequestHandler인 경우, 정적인 처리를 위한 핸들러이므로 이것에 대한 특정 정보는 따로 처리를 해줘야한다.
    • 공통적인 정상처리 및 예외처리를 하기 위해서는 afterCompletion() 메서드를 사용
      • Controller에서 예외 발생시, postHandle()은 호출이 되지 않기에 공통적인 예외처리는 afterCompletion() 메서드를 이용
@Slf4j
public class MyInterceptor implements HandlerInterceptor{
    
    private static final String ID = "specify Id";
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object Handler) throws Exception{
        
        String requestURI = request.getRequestUri();
        
        String uuid = UUID.randomUUID().toString();
        request.setAttribute(ID, uuid);
        
        if ( handler instaceOf HandlerMethod ){
            HandlerMethod hm = (HandlerMethod) handler;        
        }
        
        return true;
    }
    
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle [{}]", modelAndView); // Controller 예외시 호출 되지 않음
    }
    
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
        String requestURI = request.getRequestURI();
        String logId = request.getAttribute(ID);
        
        log.info("RESPONSE {} {}", logId, requestURI);
        
        if ( ex != null ){
            log.error("afterCompletion error!", ex);
        }
    }
}

 

 

 

Intercept 등록 방법

  • addInterceptors() 메서드를 @Override 하여 구현한 Intercept를 등록
  • WebMvcConfigurer의 인터페이스를 이용하여 WebConfig 구성
  • order() 메서드를 이용하여 우선순위 설정
  • addPathPatterns() 메서드를 사용하여 적용 범위 설정
  • excludePathPatterns() 메서드를 사용하여 적용하지 않을 범위 설정
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error");
    }

}