1. Filter
- Servlet 스펙에 속한 기능 (Spring이 아닌 Java EE 표준)
- DispatcherServlet 이 실행되기 이전 단계부터 동작 가능
- 요청(Request)와 응답(Response)에 대해 전역적 처리 가능
- 주로 인코딩, 보안, 로깅, 인증 같은 전처리/후처리 용도로 사용
- javax.servlet.Filter 인터페이스 구현
- Spring이 아닌 서블릿 컨테이너 단위에서 동작
- 스프링 컨텍스트와 무관하게 동작 (즉, Spring Bean 주입을 직접 쓰기 어려움)
2. Interceptor
- Spring MVC에서 제공하는 기능
- Handler(Controller) 실행 전후를 가로챔
- 주로 인증/인가, 세션체크, 로깅, 컨트롤러에 전달할 데이터 가공 등에 사용
- HandlerInterceptor 인터페이스 구현
- DispatcherServlet이 요청을 처리하고 Controller(Handler) 가 실행되기 직전/직후에 동작
- Spring Context에 속함 (Spring Bean을 자유롭게 주입 가능)
- 필터보다 더 세밀한 제어 가능 (특히 컨트롤러 레벨 단위)
3. 실행 순서
[Client Request]
↓
Filter (Servlet 레벨)
↓
DispatcherServlet (Spring MVC 진입)
↓
Interceptor (Spring 레벨)
↓
Controller (핸들러)
↓
Interceptor (afterController)
↓
View Resolver
↓
Filter (response 후처리)
↓
[Client Response]
4. Filter가 있는데 왜 Interceptor를 사용하는가?
1) Spring Bean 접근성
- Filter는 서블릿 레벨에서 동작 -> Spring Bean 주입 어렵다.
- Interceptor는 Spring Bean 사용 가능 -> DB, Service, Repository 사용 가능.
2) 적용 범위 차이
- Filter는 애플리케이션 전역(모든 요청/응답 대상)
- Interceptor는 Spring MVC의 컨트롤러 요청에만 적용 (정적 리소스 제외 가능)
3) 제어 지점
- Filter 는 단순히 요청/응답 스트림을 가공하는 수준
- Interceptor는 preHandle, postHandle, afterCompletion 같은 세밀한 단계별 제어 제공
5. 정리하면
- Filter → 전역적인 요청/응답 처리 (보안, 인코딩, CORS, 로깅)
- Interceptor → 컨트롤러 호출 전후 로직 (인증/인가, 로깅, 데이터 가공)
- 따라서 Spring 기반 개발 시 컨트롤러 단위 제어가 필요하면 Interceptor를 주로 사용합니다.
Filter 예제 (Servlet 레벨)
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
@WebFilter("/*") // 모든 요청에 대해 적용
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("LoggingFilter 초기화");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
System.out.println("Filter: 요청이 들어왔습니다.");
// 다음 필터 또는 DispatcherServlet으로 요청을 전달
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.out.println("Filter: 응답 완료, 처리시간 = " + (endTime - startTime) + "ms");
}
@Override
public void destroy() {
System.out.println("LoggingFilter 종료");
}
}
- 모든 요청(/*)에 대해 동작
- Spring 컨텍스트에 의존하지 않고 순수 서블릿 필터로 동작
Interceptor 예제 (Spring MVC 레벨)
1) Interceptor 클래스 작성
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoggingInterceptor implements HandlerInterceptor {
// 컨트롤러 실행 전
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("Interceptor: 컨트롤러 실행 전 - 요청 URI = " + request.getRequestURI());
return true; // true → 컨트롤러 실행 계속 진행, false → 요청 중단
}
// 컨트롤러 실행 후 (뷰 렌더링 전)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, org.springframework.web.servlet.ModelAndView modelAndView)
throws Exception {
System.out.println("Interceptor: 컨트롤러 실행 후 - View 렌더링 전");
}
// 요청 완료 후 (뷰 렌더링까지 끝난 뒤)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception {
System.out.println("Interceptor: 요청 완료 후");
}
}
2) Interceptor 등록
import org.springframework.beans.factory.annotation.Autowired;
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 LoggingInterceptor())
.addPathPatterns("/**") // 모든 요청에 적용
.excludePathPatterns("/css/**", "/js/**"); // 정적 리소스 제외
}
}
- 컨트롤러 실행 전후에 동작 (preHandle, postHandle, afterCompletion)
- Spring Bean 주입 가능
비교 요약
구분 | Filter | Interceptor |
소속 | Servlet 스펙 (Java EE) | Spring MVC |
적용 범위 | 애플리케이션 전역 (모든 요청) | 컨트롤러 요청만 |
실행 시점 | DispatcherServlet 실행 전후 | DispatcherServlet 이후, 컨트롤러 전후 |
Bean 주입 | 불가 (Spring 컨텍스트 밖) | 가능 (Spring Bean 자유롭게 사용) |
주 사용처 | 인코딩, CORS, 인증, 로깅 등 전역적 처리 | 인증/인가, 세션체크, 로깅, 데이터 가공 |
즉, 전역적 요청/응답 처리는 Filter, 컨트롤러 단위 로직 제어는 Interceptor를 쓰는 게 적합하다.
일반 Filter 와 Spring Security Filter의 차이
- 일반 Filter
- 서블릿 컨테이너(Tomcat 등)가 직접 관리
- web.xml 또는 @WebFilter 등록 -> Spring Context 밖에서 실행
- 따라서 @Autowired 같은 Spring DI 사용 불가
- Spring Security Filter
- Spring 내부에서 FilterChainProxy 라는 단일 Filter를 서블릿에 등록
- 이 FilterChainProxy 안에 여러 Security Filter들 (UsernamePasswordAuthenticationFilter, JwtAuthenticationFilter 등)을 Spring Bean으로 관리
- 즉, 보이는 건 단일 Servlet Filter 지만, 실제 동작은 Spring이 만든 Bean들을 체인처럼 연결해서 실행
구조적으로 이해하기
Client Request
↓
Servlet Container
↓
DelegatingFilterProxy (서블릿에 등록된 Filter, Spring이 제공)
↓
FilterChainProxy (Spring Security 내부 FilterChain)
↓
[ Spring Security Filters (모두 Spring Bean) ]
- SecurityContextPersistenceFilter
- UsernamePasswordAuthenticationFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
...
↓
DispatcherServlet → Interceptor → Controller
DelegatingFilterProxy 가 핵심
Spring Security가 Servlet Filter 임에도 Spring Bean을 사용할 수 있는 이유는 바로 DelegatinFilterProxy 때문이다.
서블릿 컨테이는 Spring을 모르기 때문에 일반 Filter를 실행해야 함.
Spring은 DelegatingFilterProxy를 Servlet Filter로 등록.
DelegatingFilterProxy는 내부적으로 Spring ApplicationContext에서 관리하는 Bean(FilterChainProxy)을 찾아서 위임(delegate).
따라서 실제로 실행되는 Filter들은 전부 Spring Bean으로 관리되므로 DI가 가능.
Spring Security Filter 등록 방식
// web.xml or Spring Boot 자동 등록
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
마무리
- 일반 Filter: 서블릿 컨테이너 관리, Spring Bean 직접 사용 불가.
- Spring Security Filter: DelegatingFilterProxy → FilterChainProxy 구조 덕분에 Spring Bean으로 관리, 따라서 DI 가능.
- 즉, 겉으로는 Servlet Filter지만 속은 Spring Bean 체인이라고 이해하면 됩니다.
댓글