이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Filter, Interceptor, AOP는 요청 처리 흐름의 어느 위치에서 실행되는가?
- 같은 공통 처리라도 실행 위치에 따라 선택 도구가 달라지는 이유는 무엇인가?
- “로그를 남긴다” 같은 요구를 access log, handler log, method timing으로 나누어 생각하는 방법은 무엇인가?
개요
Filter, Interceptor, AOP는 모두 공통 처리를 분리하는 도구다. 그래서 처음에는 서로 대체 가능한 것처럼 보인다. 하지만 실제로는 실행 위치와 사용할 수 있는 정보가 다르다. 이 차이가 설계 판단의 핵심이다.
먼저 요청 처리 체인을 머릿속에 고정해야 한다. HTTP 요청은 Servlet Container에서 시작해 FilterChain을 지나 DispatcherServlet으로 들어오고, HandlerMapping과 HandlerAdapter를 거쳐 Controller 메서드로 간다. Service Bean 호출은 그 이후 애플리케이션 내부 협력이다.
위치 비교
Client
-> Servlet Container
-> Servlet Filter
-> Spring Security FilterChain
-> DispatcherServlet
-> HandlerMapping
-> HandlerInterceptor
-> Controller
-> Service Bean
-> AOP Proxy
-> Target MethodFilter는 DispatcherServlet 이전이다. 그래서 요청이 어떤 Controller로 갈지 아직 모를 수 있지만, 요청과 응답 자체를 가장 앞에서 다룰 수 있다.
Interceptor는 HandlerMapping 이후다. 어떤 Controller 또는 HandlerMethod가 실행될지 알 수 있다. 대신 Controller에 도달하기 전 Security Filter에서 이미 차단된 요청은 Interceptor까지 오지 않을 수 있다. 또한 MVC path matching과 Security matcher가 완전히 같은 계층이 아니므로, Interceptor를 보안 경계로 삼으면 보호 범위가 어긋날 수 있다.
AOP는 Spring Bean 메서드 호출 경계다. HTTP 요청이 아니어도 동작할 수 있다. 배치, 메시지 리스너, 다른 서비스 호출에서도 같은 Bean 메서드 정책을 적용할 수 있다.
요구사항별 선택
access log가 필요하면 Filter가 자연스럽다. 요청 시작부터 응답 완료까지의 전체 시간, status, method, URI, header를 가장 앞단에서 볼 수 있기 때문이다.
Controller 메서드 애너테이션을 보고 로깅 정책을 바꾸고 싶으면 Interceptor가 더 낫다. HandlerMethod를 통해 Controller와 메서드 정보를 얻을 수 있다.
Service 메서드의 실행 시간이나 트랜잭션 정책을 다루려면 AOP가 맞다. HTTP 요청이 아닌 호출에도 동일하게 적용되기 때문이다.
코드 예제
public class RequestIdFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
String requestId = UUID.randomUUID().toString();
response.setHeader("X-Request-Id", requestId);
filterChain.doFilter(request, response);
}
}이 코드는 HTTP 응답 header를 다루므로 Filter가 자연스럽다.
public class HandlerLoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod handlerMethod) {
log.info("handler={}", handlerMethod.getMethod().getName());
}
return true;
}
}이 코드는 HandlerMethod 정보가 필요하므로 Interceptor가 자연스럽다.
실전 팁
- 필요한 정보가 request/response 원본인지, Handler 정보인지, Bean 메서드 정보인지 먼저 묻는다.
- Security는 Interceptor보다 FilterChain에서 해결하는 것이 표준이다.
- Interceptor는 Handler 정보가 필요할 때 쓰고, 인증/인가의 최종 방어선으로 삼지 않는다.
- Controller 응답 body를 바꾸려면 Interceptor보다
ResponseBodyAdvice를 검토한다. - Service method timing은 Filter의 요청 시간과 다르다. 둘을 같은 지표로 보지 않는다.
- 같은 기능을 여러 경계에 중복 적용하지 않는다.
위험 신호!
- 인증을 Interceptor로 구현하면서 Spring Security와 별도로 SecurityContext를 만든다.
- Interceptor path pattern을 보안 규칙처럼 사용해 Security matcher와 다른 보호 범위를 만든다.
- access log와 service timing을 같은 AOP로 모두 처리하려 한다.
- Filter에서 HandlerMethod 애너테이션을 억지로 찾는다.
- Interceptor에서 response body를 문자열로 직접 조작하려 한다.
- AOP Pointcut이 Controller, Service, Repository 전체를 넓게 덮고 의도가 불분명하다.
확인 질문
확인 질문
- Filter와 Interceptor의 위치 차이는 무엇인가?
- Filter는 DispatcherServlet 이전 Servlet 경계이고, Interceptor는 HandlerMapping 이후 MVC 경계다.
- AOP가 HTTP 요청이 아닌 곳에서도 동작할 수 있는 이유는 무엇인가?
- HTTP가 아니라 Spring Bean 메서드 호출 경계에 적용되기 때문이다.
- HandlerMethod 애너테이션이 필요한 공통 처리는 어떤 도구가 자연스러운가?
- HandlerInterceptor가 자연스럽다.