왜 AOP에서 Interceptor로 변경했는가?
AOP가 메서드 수준에서 로깅을 처리하는 반면, Interceptor는 요청/응답 처리 흐름에 맞게 로깅을 적용할 수 있다.
처음에 AOP를 적용했을 때, 모든 API 호출에 대한 로깅을 DB에 저장하였다. 하지만 여기서 큰 문제점이 발생한다. 어떠한 컨트롤러의 메소드도 타지 않는 경우 (아예 이상한 url 로 요청을 보내거나 http method 를 이상하게 보내는 경우)는 로깅이 불가능하다는 점이다!
왜 필터가 아닌 인터셉터 방식인가?
- 인터셉터(Interceptor)는 디스패처 서블릿(Dispatcher Servlet)이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있다. (스프링 컨테이너 내부에서 관리)
- 필터(FIlter)는 디스패처 서블릿(Dispatcher Servlet)에 요청이 전달되기 전/후에 URL 패턴에 맞는 모든 요청에 부가작업을 처리할 수 있는 작업을 말한다. (필터는 스프링 범위 밖에서 처리가 되는 것이다)
- 따라서 서버에서는 모든 요청에 대한 로깅이 아니라, API 호출에 대한 로깅을 하고 싶었기 때문에 인터셉터 방식을 사용하였다.
1. WebConfig
- addInterceptors(InterceptorRegistry registry): 인터셉터를 등록할 수 있습니다. 특정 URL 패턴에 대해 인터셉터를 지정할 수 있습니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor())
.order(1) // (1)
.addPathPatterns("/**") // (2)
.excludePathPatterns("/error"); // (3)
}
}
2. LogUtility 클래스
public class LogUtility {
@Getter
@AllArgsConstructor
public static class LogInfo {
private String uuid;
private String requestURI;
}
private static final ThreadLocal<LogInfo> REQUEST_INFO = new ThreadLocal<>(); // (1)
public static void set(String uuid, String requestURI) {
REQUEST_INFO.set(new LogInfo(uuid, requestURI));
}
public static String getUUID() {
return REQUEST_INFO.get().getUuid();
}
public static String getRequestURI() {
return REQUEST_INFO.get().getRequestURI();
}
public static void remove() {
REQUEST_INFO.remove();
}
}
3. RequestInterceptor
preHandle 메서드
- 요청이 컨트롤러에 도달하기 전에 실행됩니다.
afterCompletion 메서드
- 요청 처리 후, 뷰 렌더링이 끝난 후에 실행됩니다. 즉, 컨트롤러에서 요청을 처리하고, 뷰를 렌더링한 뒤에 호출됩니다.
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uuid = UUID.randomUUID().toString(); // (1)
String requestURI = request.getMethod() + " " + request.getRequestURL();
log.info("Request [{}][{}]", uuid, requestURI); // (2)
LogUtility.set(uuid, requestURI); // (3)
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("Response {} [{}][{}]", response.getStatus(), LogUtility.getUUID(), handler); // (4)
LogUtility.remove(); // (5)
}
}
webConfig 메서드
WebConfig 클래스는 WebMvcConfigurer 인터페이스를 구현하여, 스프링 MVC의 설정을 커스터마이즈합니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor())
.order(1) // (1)
.addPathPatterns("/**") // (2)
.excludePathPatterns("/error"); // (3)
}
}
'프로젝트 > Ku:room' 카테고리의 다른 글
Spring REST Docs와 Swagger-UI 결합하기 (0) | 2025.02.19 |
---|---|
필터(Filter) vs 인터셉터(Interceptor) vs AOP 차이점 (0) | 2025.02.19 |
개발자 겸 디자이너 겸 PM 도전기 (0) | 2025.02.14 |
nGrinder 스크립트 검증 도중 Connection refused (1) | 2025.02.13 |
QueryDSL로 동적 쿼리 가져오기 (0) | 2025.02.13 |