공통 관심사라는 것은, 여러 위치에서 반복적으로 사용되는 부가 기능(인증, 로깅, 보안 등)을 의미합니다. 비즈니스 로직과 별개로 동작하고 코드 중복과 유지보수 문제를 일으킬 수 있습니다.
대표적인 예시로는, 로그인이 되어있는 유저만 특정 API를 사용 가능한 것이 있습니다. 모든 컨트롤러 또는 메서드에 로그인 체크 로직을 넣게 된다면 코드가 지저분해지고, 변경이 될 경우에는 일괄로 수정하기가 힘들어집니다.
위에서 이야기했던 공통 관심사를 한 곳에서 처리할 수 있는 것이 필요합니다. 모든 컨트롤러와 메서드에 로직을 넣으면 유지보수가 힘들어지는데, 한 곳에서 처리하게 된다면 코드의 중복도 줄어들게 되고, 유지보수성이 향상될 것입니다.
또한, 비즈니스 로직과 분리되어서 핵심 로직은 컨트롤러에 집중하고 부가적인 로직은 필터 또는 인터셉터에 위임할 수 있습니다.
Web Application의 모든 HTTP 요청 또는 응답을 가로채 공통된 작업을 처리할 수 있는 표준 Servlet Component입니다.
클라이언트 요청 -> Filter -> DispatcherServlet -> Interceptor -> Controller
여기서 주목해야 할 점은 Filter가 DispatcherServlet보다 더 앞단에서 동작한다는 것입니다. Spring Security는 인증 및 인가 등의 보안 관련 처리를 가장 앞단에서 수행하며, 이를 통해 컨트롤러로 불필요한 요청을 차단해서 보안성과 성능을 모두 확보할 수 있습니다.
public class CustomFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 요청 전 처리
chain.doFilter(request, response); // 다음 필터로 요청 전달
// 응답 후 처리
}
}
| 특징 | Filter | Interceptor |
|---|---|---|
| 관리 주체 | 웹 컨테이너 | 스프링 컨테이너 |
| 동작 위치 | DispatcherServlet 이전 | DispatcherServlet 이후 |
| 적용 대상 | 모든 HTTP 요청 및 응답 | 스프링 MVC Controller에 한정 |
| 용도 | 인코딩, 인증 및 인가, 로깅, 보안 등 | 인증 및 인가, 로깅, 권한 체크 등 |
| 객체 조작 | Request/Response를 직접 조작 가능 | 직접 조작이 불가능, 데이터 가공 및 흐름 제어 |
간단하게 말하면 Filter는 저수준, 전역적, 스프링과 무관한 기능에 적합하며, Interceptor는 스프링 MVC와 연동되는 세부로직에 적합합니다.
Java Servlet에서 HTTP 요청과 응답을 가로채고, 이를 기반으로 하여 다양한 처리 작업을 수행하는 데에 사용되는 인터페이스입니다.
@Slf4j
public class CustomFilter implements Filter {
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// 주로 HttpServletRequest로 다운캐스팅하여 사용
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
log.info("request URI={}", requestURI);
// 다음 필터(혹은 서블릿)로 제어를 넘김
chain.doFilter(request, response);
}
}
만약 chain.doFilter()를 호출하지 않으면 다음 필터나 서블릿이 실행되지 않습니다. 모든 필터의 doFilter()가 끝나면 DispatcherServlet이 호출됩니다.
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<Filter> customFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new CustomFilter());
filterRegistrationBean.setOrder(1); // 숫자가 낮을수록 우선순위 높음
filterRegistrationBean.addUrlPatterns("/*"); // 전체 URL에 적용
return filterRegistrationBean;
}
}
setOrder()로 Filter Chain 내 실행 순서를 지정할 수 있습니다. 여러 필터를 등록할 시 숫자가 작은 것부터 실행이 됩니다.
addUrlPatterns()로 필터를 적용할 URL 패턴을 지정합니다.
@Bean
public FilterRegistrationBean<FirstFilter> firstFilterRegister() {
FilterRegistrationBean<FirstFilter> registrationBean = new FilterRegistrationBean<>(new FirstFilter());
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecondFilter> secondFilterRegister() {
FilterRegistrationBean<SecondFilter> registrationBean = new FilterRegistrationBean<>(new SecondFilter());
registrationBean.setOrder(2);
return registrationBean;
}
실행 순서
: FirstFilter -> SecondFilter -> Servlet
+@
- ServletRequest는 기능이 제한적이므로 보통 HttpServletRequest로 다운캐스팅해 사용합니다.
- @WebFilter annotation으로도 필터 등록이 가능하지만, 순서 조정이 어려워 FilterRegistrationBean의 사용을 권장합니다.
자료 및 코드 출처: 스파르타 코딩클럽