스프링 인터셉터
- 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결하는 기술
- 스프링 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 |