Spring으로 웹 애플리케이션을 개발하다 보면 Filter와 Interceptor를 자주 마주칩니다. 둘 다 요청을 가로채서 뭔가를 처리한다는 점에서 비슷해 보입니다. 인증 처리를 해야 할 때, 로깅을 해야 할 때, 어떤 걸 써야 할지 고민이 될 때가 있습니다. 왜 두 가지가 따로 존재하는지, 각각 언제 사용하면 좋은지 정리해보겠습니다.
핵심 차이: 누구의 영역인가
결론부터 말하면, Filter는 Servlet의 영역이고, Interceptor는 Spring의 영역입니다.

이 흐름을 보면 감이 올 겁니다. Filter가 먼저 실행되고, 그 다음에 Spring 영역으로 들어와서 Interceptor가 실행됩니다.
Filter: Servlet 스펙
Filter는 Java Servlet 스펙에 정의된 기능입니다. Spring이 아니라 Servlet Container(Tomcat 등)가 관리합니다.
특징
jakarta.servlet.Filter인터페이스를 구현 (Spring Boot 2.x 이하는javax.servlet.Filter)- DispatcherServlet에 요청이 도달하기 전/후에 동작
- Spring Boot에서는
@Component로 등록하면 Bean 주입 가능 - 단, DispatcherServlet 이전에 실행되므로
@ControllerAdvice등 Spring MVC 기능은 사용 불가 - 모든 요청에 대해 동작 (정적 리소스 포함)
코드 예시
@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
System.out.println("[Filter] 요청 URI: " + httpRequest.getRequestURI());
// 다음 필터 또는 서블릿으로 요청 전달
chain.doFilter(request, response);
System.out.println("[Filter] 응답 완료");
}
}
Filter가 적합한 경우
- 인코딩 변환: 모든 요청의 문자 인코딩을 UTF-8로 설정
- XSS 방어: 요청 파라미터의 악성 스크립트 필터링
- CORS 처리: Cross-Origin 요청 헤더 처리
- 요청/응답 로깅: Spring과 무관하게 모든 요청을 기록
@Component
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
Interceptor: Spring MVC 스펙
Interceptor는 Spring MVC가 제공하는 기능입니다. Spring Context 내부에서 동작하기 때문에 Spring의 모든 기능을 활용할 수 있습니다.
특징
HandlerInterceptor인터페이스를 구현- DispatcherServlet이 Controller를 호출하기 전/후에 동작
- Spring Bean을 주입받아 사용 가능
- Controller에 대한 요청만 처리 (정적 리소스는 제외 가능)
코드 예시
@Component
public class AuthInterceptor implements HandlerInterceptor {
private final TokenService tokenService; // Spring Bean 주입 가능!
public AuthInterceptor(TokenService tokenService) {
this.tokenService = tokenService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (!tokenService.isValid(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false; // false를 반환하면 Controller까지 가지 않음
}
return true; // true를 반환해야 다음 단계로 진행
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// Controller 실행 후, View 렌더링 전
System.out.println("[Interceptor] Controller 처리 완료");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// View 렌더링까지 완료된 후
System.out.println("[Interceptor] 요청 처리 완전히 완료");
}
}
Interceptor 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
public WebConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 적용할 경로
.excludePathPatterns("/api/auth/**"); // 제외할 경로
}
}
Interceptor가 적합한 경우
- 인증/인가: 로그인 여부, 권한 확인
- 로그인 체크: 특정 페이지 접근 전 로그인 상태 확인
- API 호출 로깅: Controller 단위의 요청/응답 기록
- 공통 데이터 설정: 모든 Controller에 필요한 공통 데이터 주입
실행 흐름 상세
요청이 들어올 때의 전체 흐름을 보겠습니다.

예외가 발생했을 때도 차이가 있습니다.
- Filter: DispatcherServlet 밖이라
@ControllerAdvice가 예외를 잡지 못함. Filter 내에서 직접 try-catch로 처리해야 함 - Interceptor: Spring 내부라서
@ControllerAdvice로 예외를 처리할 수 있음.afterCompletion은 예외 발생 여부와 관계없이 실행됨
비교 정리
| 구분 | Filter | Interceptor |
|---|---|---|
| 스펙 | Servlet | Spring MVC |
| 관리 주체 | Servlet Container | Spring Container |
| 실행 시점 | DispatcherServlet 전/후 | Controller 전/후 |
| Spring Bean 주입 | 가능 (Spring Boot 기준) | 가능 |
| Spring MVC 기능 | 사용 불가 (@ControllerAdvice 등) | 사용 가능 |
| Request/Response 조작 | 가능 (래퍼 클래스 사용) | 제한적 |
그래서 뭘 써야 할까?
간단하게 정리하면 이렇습니다.
Filter를 선택하는 경우:
- Spring과 무관한 순수 Servlet 레벨의 처리
- 모든 요청(정적 리소스 포함)에 대한 처리
- Request/Response 자체를 변경해야 할 때 (래퍼 클래스 사용)
- 인코딩, 보안(XSS, CORS) 등 웹 애플리케이션 공통 관심사
Interceptor를 선택하는 경우:
- Controller 단위의 세밀한 제어가 필요할 때
@ControllerAdvice와 연계한 예외 처리가 필요할 때- 간단한 인증/인가 처리 (세션 체크, JWT 검증 등)
- API 요청에 대한 로깅, 모니터링
참고로 Spring Security는 Filter 기반으로 동작합니다. 본격적인 보안 처리가 필요하다면 Spring Security를 사용하는 것이 일반적입니다.
실제로 많이 쓰는 조합을 예로 들면:
Filter: 인코딩 설정, CORS, XSS 필터링
Interceptor: 로그인 체크, API 인증, 요청 로깅
둘은 경쟁 관계가 아니라 각자의 역할이 있는 것입니다. Spring 영역 밖에서 처리해야 하면 Filter, Spring 기능을 활용해야 하면 Interceptor를 선택하면 됩니다.
정리
- Filter: Servlet 스펙, DispatcherServlet 이전에 동작, 모든 요청 대상
- Interceptor: Spring MVC 스펙, DispatcherServlet 이후에 동작, Controller 요청 대상
- 둘 다 Spring Boot에서는 Bean 주입이 가능하지만, Filter는
@ControllerAdvice같은 Spring MVC 기능을 사용할 수 없다 - Request/Response 자체를 조작하거나 모든 요청을 처리해야 하면 Filter
- Controller 단위 제어나 Spring MVC 기능 연계가 필요하면 Interceptor
- 둘 다 같이 사용해도 된다. 역할에 맞게 선택하면 된다
'프레임워크 > 스프링' 카테고리의 다른 글
| AOP(관점 지향 프로그래밍): 어떻게 메소드 실행 전후에 로그를 남길까? (0) | 2025.12.24 |
|---|---|
| 스프링의 Scope(스코프): 싱글톤(Singleton)과 프로토타입(Prototype)의 결정적 차이 (0) | 2025.12.23 |
| DispatcherServlet의 역할: 요청이 들어와서 응답이 나갈 때까지의 여정 (1) | 2025.12.19 |
| Spring Bean의 생명주기(Lifecycle) - 객체 생성부터 소멸까지 (0) | 2025.12.18 |
| DI(의존성 주입)과 IoC(제어의 역전): 왜 우리가 직접 new를 안 쓸까? (0) | 2025.12.18 |