쌓고 쌓다
[스프링 부트] 모든 요청 로그 남기기(인터셉터) 본문
스프링 인터셉터
스프링 인터셉터 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
- 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기에, 디스패처 서블릿 이후에 등장한다.
- 인터셉터 체인으로 인터셉터 다음에 인터셉터를 축
스프링 인터셉터 제한
1. 로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
2. 비 로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출 X)
스프링 인터셉터 인터페이스
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 {
}
}
- preHandle : 컨트롤러 호출 전 호출 (핸들러 어댑터 호출 전)
- 리턴 값이 true라면 다음으로 진행, false라면 나머지 인터셉터와 핸들러 어댑터를 호출하지 않음.
- postHandle : 컨트롤러 호출 후에 호출
- afterCompletion : 뷰가 렌더링 된 이후에 호출
스프링 인터셉터 예외 발생시
- 컨트롤러에서 예외 발생시 postHandle은 호출되지 않는다.
- afterCompletion은 예외가 발생하더라도 항상 호출된다. Exception ex 파라미터로 예외 정보를 포함한다.
afterCompletion은 예외가 발생해도 항상 호출되므로 예외와 무관한 공통처리에 유용하다.
스프링 인터셉터 - 요청 로그
LogInterceptor
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.util.UUID;
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
private static 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();
// 인터셉터는 호출 시점이 분리되어 있어 preHandler에서 만든 값을 postHandle, afterCompletion에서
// 사용하려면 request에 담아두어야 한다.
// LogInterceptor는 싱글톤처럼 사용되기에 멤머변수로 값을 저장하면 위험하다.
request.setAttribute(LOG_ID, uuid);
// 핸들러 매핑에 따라 핸들러 정보가 달라진
// @Controller, @RequestMapping은 HandlerMethod가 넘어옴
// /resources/static 같은 정적 리소스라면 ResourceHttpRequestHandler가 넘어옴
if(handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
}
log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
return true; // 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 uuid = (String)request.getAttribute(LOG_ID);
log.info("RESPONSE [{}][{}]", uuid, requestURI);
if(ex!=null) {
log.error("afterCompletion error!", ex);
}
}
}
- preHandle
- LOG_ID : 인터셉터는 호출 시점이 완전히 분리되어 있어 preHandle에서 사용한 값을 postHandle, afterCompletion에서 사용하려면 어딘가에 담아 사용해야한다.
- request에 담아두어 사용.
- 리턴 값이 true여야 정상 호출로 다음 인터셉터나 컨트롤러를 이어서 호출한다.
- LOG_ID : 인터셉터는 호출 시점이 완전히 분리되어 있어 preHandle에서 사용한 값을 postHandle, afterCompletion에서 사용하려면 어딘가에 담아 사용해야한다.
- afterCompletion
- 예외가 발생하여 postHandle이 호출되지 않을 수 있으므로 afterCompletion에 종료 로그를 찍는다.
인터셉터 등록 - WebConfig
import com.example.board.filter.LogFilter;
import com.example.board.interceptor.LogInterceptor;
import jakarta.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error", "/js/**");
}
}
- WebMvcConfigurer가 제공하는 addInterceptors를 통해 인터셉터를 등록할 수 있다.
- order : 인터셉터의 호출 순서 (숫자가 낮을수록 먼저 호출)
- addPathPatterns : 인터셉터를 적용할 URL 패턴 지정
- excludePathPatterns : 인터셉터에서 제외할 패턴
실행 로그 결과
'프로그래밍 > spring' 카테고리의 다른 글
[스프링 부트] 회원 정보(세션)로 게시글 작성 - 32 (0) | 2023.10.01 |
---|---|
HTML에서 날짜 입력 받아 서버에서 LocalDate로 받기 (0) | 2023.09.20 |
[스프링 부트] 카카오 로그인 회원 저장 방법- 31 (0) | 2023.09.17 |
스프링 부트에서 JS 파일 경로 (404 에러) (0) | 2023.09.17 |
application.properties 값을 필드에 사용하는 법 (0) | 2023.09.16 |
Comments