Spring Boot - Servlet Filter 언제 어떻게 써야 할까?
서블릿 필터의 개념과 사용 방법
웹 애플리케이션을 개발하다 보면 여러 컨트롤러에 걸쳐 공통적으로 처리해야 할 작업들이 생깁니다. 예를 들어, 모든 요청에 대한 로깅, 특정 요청에 대한 인증/인가 처리, 요청/응답 데이터의 전처리 후처리 작업이 있습니다. 이런 로직을 필요한곳에 매번 작성하는 것은 중복과 유지보수 비용이 많이 발생하게 됩니다.
Servlet Filter를 사용하여 보다 간단히 처리할 수 있습니다. 스프링 부트 환경에서 이 필터를 어떻게 구현하고, 등록하는 것인지 정리하였습니다.
1. 기본 필터 만들어보기: 요청 시간 측정 로거
먼저 간단하면서도 실용적인 필터를 하나 만들어 보겠습니다. 모든 API 요청이 들어올 때 시작 시간을 기록하고, 요청 처리가 끝났을 때 종료 시간을 기록하여 총 소요 시간을 로그로 남기는 필터입니다.
jakarta.servlet.Filter
인터페이스를 상속받고 doFilter()
메서드를 오버라이드해야 합니다.
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.UUID;
@Slf4j
public class RequestLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String requestId = UUID.randomUUID().toString();
log.info("REQUEST [{}][{}]", requestId, requestURI);
long startTime = System.currentTimeMillis();
// 다음 필터 또는 서블릿으로 요청 전달
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - startTime;
log.info("RESPONSE [{}][{}][{}ms]", requestId, requestURI, duration);
}
}
💡 잠깐!
chain.doFilter(request, response)
코드가 바로 필터의 핵심입니다. 이 코드를 기준으로 전(pre-processing)에는 요청 처리 전 공통 기능을, 후(post-processing)에는 응답 처리 후 공통 기능을 수행할 수 있습니다.
2. 필터 등록하기: 두 가지 방법, 언제 무엇을 쓸까?
이제 위에서 만든 RequestLoggingFilter
를 스프링 부트가 인식하도록 등록해야 합니다. 등록 방법은 크게 두 가지가 있으며, 각각의 장단점과 사용 사례가 명확합니다.
방법 1: @Component
- 가장 간단하고 빠른 방법
@Component
@Slf4j
public class RequestLoggingFilter implements Filter {
// ... (doFilter 로직은 위와 동일)
}
가장 쉬운 방법이며 필터 구현체에 @Component
어노테이션을 사용하여 스프링 빈으로 등록합니다.
- 애플리케이션의 모든 요청(
/*
)에 대해 필터를 적용하고 싶을 때 사용합니다. 따라서 특정 URL 패턴에만 필터를 적용하거나 세밀한 순서 제어가 어렵습니다.@Order
어노테이션을 사용하여 필터간의 순서를 간단하게 제어할 수 있습지만 특정 URL에만 필터 적용은 불가합니다.
- 빠르게 프로토타이핑하거나 간단한 공통 로깅 필터를 추가할 때 매우 유용합니다.
방법 2: FilterRegistrationBean
- 세부 설정
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RequestLoggingFilter> loggingFilter() {
FilterRegistrationBean<RequestLoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestLoggingFilter()); // 등록할 필터 지정
registrationBean.addUrlPatterns("/api/*"); // '/api/'로 시작하는 URL에만 적용
registrationBean.setOrder(1); // 필터 체인에서 1번 순서로 실행
registrationBean.setName("RequestLoggingFilter"); // 필터 이름 지정
return registrationBean;
}
}
적용할 URL 패턴이나 실행 순서를 명확하게 제어해야 하는 경우 FilterRegistrationBean
을 사용합니다.
@Configuration
클래스를 생성하고 FilterRegistrationBean
을 반환하는 @Bean
메서드를 정의합니다.
- 특정 경로(
/api/*
,/admin/*
등)에만 적용해야 할 때 - 여러 필터가 존재하여 실행 순서를 명확하게 정의해야 할 때 (로깅 필터 -> 인증 필터)
- 특정 환경(개발,운영)에 따라 필터를 활성화, 비활성화해야 할 때
3. 마무리
구분 | @Component 방식 |
FilterRegistrationBean 방식 |
---|---|---|
적용 범위 | 모든 URL (/* ) |
특정 URL 패턴 지정 가능 |
적용 목적 | 간단한 글로벌 필터 | 세밀한 제어가 필요한 상황 |
실행 순서 | @Order 로 제어 (제한적) |
setOrder 로 명시적 제어 (권장) |
편의성 | 매우 간편함 | 설정 코드 필요 |
- 서블릿 레벨에서 동작. 스프링 컨텍스트와 무관하게 동작하며, 주로 보안(Spring Security), 인코딩, 로깅 등 저수준의 공통 기능에 사용됩니다.
- “일단
@Component
로 간단하게 시작하고 URL 패턴이나 순서 제어가 필요해지면FilterRegistrationBean
으로 전환을 고려하면 됩니다.