본문 바로가기
Spring/MVC

스프링 인터셉터

by o3oppp 2024. 7. 11.
스프링 인터셉터
  • 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결하는 기술
  • 스프링 MVC가 제공하는 기술

스프링 인터셉터 흐름
  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
  • 스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출

스프링 인터셉터 제한
  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 // 로그인 사용자
  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단 시 컨트롤러 호출 X)

스프링 인터셉터 체인
  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터1 -> 스프링 인터셉터2 -> 컨트롤러
  • 스프링 인터셉터는 체인으로 구성
  • 중간에 인터셉터를 자유롭게 추가 가능(순서 지정 가능)

HandlerInterceptor 인터페이스
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()하나만 제공하나 인터셉터는 컨트롤러 호출 전, 호출 후, 요청 완료 이후와 같이 단계적으로 제공
  • 서블릿 필터의 경우 단순히 request, response만 제공했지만, 인터셉터는 어떤 컨트롤러(handler)가 호출되는지 호출 정보 제공
  • 또한 modelAndView가 반환되는지 응답 정보도 받을 수 있음
  • preHandle : 컨트롤러 호출 전에 호출(정확히는 핸들러 어댑터 호출 전에 호출)
    • 응답값이 true : 다음으로 진행
    • 응답값이 false : 나머지 인터셉터, 핸들러 어댑터도 호출 X
    • 항상 호출
  • postHandle : 컨트롤러 호출 후에 호출(정확히는 핸들러 어댑터 호출 후에 호출)
    • 컨트롤러에서 예외 발생 시 해당 메소드는 호출되지 않음
  • aterCompletion : 뷰가 렌더링 된 이후에 호출
    • 항상 호출
    • 컨트롤러에서 예외 발생 시 해당 메소드에서 예외 정보를 포함하여 호출

인터셉터를 사용하여 요청 로그 남기기

1. 인터셉터 구현

@Slf4j
public class LogInterceptor implements HandlerInterceptor {

    public 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);

        // @RequestMapping : HandlerMethod
        // 정적 리소스(/resource/static 과 같은 것 ) : ResourceHttpRequestHandler
        if(handler instanceof HandlerMethod){
            HandlerMethod hm = (HandlerMethod) handler;// 호출할 컨트롤러 메서드의 모든 정보가 포함
        }

        log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
        return true; // handler 호출
    }

    @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();
        Object logId = (String) request.getAttribute(LOG_ID);
        log.info("RESPONSE [{}][{}][{}]", logId, requestURI, handler);

        if(ex != null){
            log.error("afterCompletion error!!", ex);
        }
    }
}

2. 인터셉터 적용

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()) // 인터셉터 등록
                .order(1) // 순서 지정
                .addPathPatterns("/**") // 적용 URL 패턴
                .excludePathPatterns("/css/**","/*.ico","/error"); // 제외 URL 패턴
    }
}

3. 실행


인터셉터를 사용하여 인증 체크

1. 인터셉터 구현

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("인증 체크 인터셉터 실행 {}", requestURI);
        HttpSession session = request.getSession();

        if(session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null){
            log.info("미인증 사용자 요청");
            response.sendRedirect("login?redirectURL=" + requestURI);
            return false;
        }
        return true;
    }
}

2. 인터셉터 적용

@Configuration
public class WebConfig implements WebMvcConfigurer {

   @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()) // 인터셉터 등록
                .order(1) // 순서 지정
                .addPathPatterns("/**") // 적용 URL 패턴
                .excludePathPatterns("/css/**","/*.ico","/error"); // 제외 URL 패턴

        registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
                .excludePathPatterns("/","/members/add","/login","/logout","/css/**","/*.ico","/error");
    }
}

3. 실행

미인증 사용자
인증 사용자


PathPattern
? 한 문자 일치
* 경로(/) 안에서 0개 이상의 문자 일치
** 경로 끝까지 0개 이상의 경로(/) 일치
{spring} 경로(/)와 일치하고 spring이라는 변수로 캡처
{spring:[a-z]+} [a-z]+ 와 일치하고, "spring" 경로 변수로 캡처
{*spring} 경로가 끝날 때 까지 0개 이상의 경로(/)와 일치하고 spring이라는 변수로 캡처

'Spring > MVC' 카테고리의 다른 글

HttpEntity  (0) 2024.07.21
서블릿 예외 처리  (0) 2024.07.20
서블릿 필터  (0) 2024.07.11
Bean Validation  (0) 2024.06.29
Validator  (0) 2024.06.28