5.4 필터(Filter)와 인터셉터(Interceptor)
웹 애플리케이션에서 공통적인 요청 처리(인증, 데이터 압축, 로깅 등)를 위해 사용되는 두 개의 강력한 도구인 필터와 인터셉터의 차이점과 사용법을 배웁니다.
1. 필터(Filter)
필터는 서블릿 컨테이너(Servlet Container) 수준에서 실행되는 구성 요소입니다. 스프링의 DispatcherServlet에 요청이 도달하기 전과 후의 작업을 처리합니다.
특징
- J2EE 표준 스펙에 정의되어 있습니다.
- 인코딩 변환, 보안 필터(Spring Security), XSS 방어 등에 주로 사용됩니다.
- 요청(Request)과 응답(Response) 객체를 통째로 조작하거나 다른 객체로 교체할 수 있습니다.
구현 예제 (로깅 필터)
@Component
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Filter: DispatcherServlet 도달 전");
chain.doFilter(request, response); // 다음 필터나 서블릿으로 전달
System.out.println("Filter: 클라이언트에게 응답 직전");
}
}
2. 인터셉터(Interceptor)
인터셉터는 스프링 컨텍스트(Spring Context) 내부에서 실행되는 구성 요소입니다. DispatcherServlet이 컨트롤러를 호출하기 전과 후에 동작합니다.
특징
- 스프링 MVC 고유의 기능입니다.
- 로그인 체크, 권한 체크, API 호출 로그 기록 등에 주로 사용됩니다.
- 스프링의 모든 Bean에 접근할 수 있어 비즈니스 로직과 밀접한 처리가 가능합니다.
구현 예제 (권한 인터셉터)
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("Interceptor: 컨트롤러 호출 전 (preHandle)");
// false를 리턴하면 컨트롤러로 요청이 전달되지 않음
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("Interceptor: 컨트롤러 작업 완료 후 (postHandle)");
}
}
3. 필터 vs 인터셉터 비교
| 구분 | 필터 (Filter) | 인터셉터 (Interceptor) |
|---|---|---|
| 관리 주체 | 서블릿 컨테이너 (Tomcat 등) | 스프링 컨테이너 (Spring MVC) |
| 실행 위치 | DispatcherServlet 밖 | DispatcherServlet 안 (Controller 전후) |
| 접근 객체 | ServletRequest, ServletResponse | HttpServletRequest, HttpServletResponse, Handler |
| 주요 용도 | 보안, 인코딩, XSS 방어 (공통 기능) | 세션 체크, 권한 관리, 상세 로그 (비즈니스적) |
4. 실행 순서
- HTTP 요청 -> Filter(
doFilter전반부) - DispatcherServlet 도달
- Interceptor(
preHandle) - Controller 실행
- Interceptor(
postHandle) - 응답 생성 (View)
- Interceptor(
afterCompletion) - Filter(
doFilter후반부) -> HTTP 응답
5. 심화: 필터에서 Request Body 읽기 (ContentCachingRequestWrapper)
일반적인 HttpServletRequest는 생성된 스트림(InputStream)을 한 번만 읽을 수 있습니다. 로그를 남기기 위해 필터에서 본문(Body)을 읽어버리면, 정작 컨트롤러에서 본문을 읽지 못하는 문제가 발생합니다. 이를 해결하기 위해 스프링이 제공하는 ContentCachingRequestWrapper를 사용합니다.
래핑 필터 구현 예제
@Component
public class CachingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 요청과 응답을 캐시 가능한 래퍼 객체로 감쌉니다.
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
// 래핑된 객체를 다음 체인으로 전달
filterChain.doFilter(requestWrapper, responseWrapper);
// 비즈니스 로직(컨트롤러) 수행 후에 본문을 읽을 수 있습니다.
String requestBody = new String(requestWrapper.getContentAsByteArray());
System.out.println("요청 본문: " + requestBody);
// 마지막에 반드시 실제 응답 스트림으로 복사해 주어야 클라이언트가 응답을 받습니다.
responseWrapper.copyBodyToResponse();
}
}
🎯 핵심 요점
- 필터 는 웹 표준 기술로, 요청 자체를 거르거나 변형할 때 유용합니다.
- 인터셉터 는 스프링 기술로, 빈(Bean)을 활용한 상세한 컨트롤러 제어에 유리합니다.
- 스프링 시큐리티는 필터 체인 을 기반으로 인증/인가를 처리합니다.