WEEK 6-10: Spring Servlet Filter

ensalada.de.pollo·2025년 5월 18일

be

목록 보기
32/44

공통 관심사(Cross-Cutting Concern)

공통 관심사라는 것은, 여러 위치에서 반복적으로 사용되는 부가 기능(인증, 로깅, 보안 등)을 의미합니다. 비즈니스 로직과 별개로 동작하고 코드 중복과 유지보수 문제를 일으킬 수 있습니다.

대표적인 예시로는, 로그인이 되어있는 유저만 특정 API를 사용 가능한 것이 있습니다. 모든 컨트롤러 또는 메서드에 로그인 체크 로직을 넣게 된다면 코드가 지저분해지고, 변경이 될 경우에는 일괄로 수정하기가 힘들어집니다.

Filter의 필요성

위에서 이야기했던 공통 관심사를 한 곳에서 처리할 수 있는 것이 필요합니다. 모든 컨트롤러와 메서드에 로직을 넣으면 유지보수가 힘들어지는데, 한 곳에서 처리하게 된다면 코드의 중복도 줄어들게 되고, 유지보수성이 향상될 것입니다.

또한, 비즈니스 로직과 분리되어서 핵심 로직은 컨트롤러에 집중하고 부가적인 로직은 필터 또는 인터셉터에 위임할 수 있습니다.

Servlet Filter

Web Application의 모든 HTTP 요청 또는 응답을 가로채 공통된 작업을 처리할 수 있는 표준 Servlet Component입니다.

동작 순서

클라이언트 요청 -> Filter -> DispatcherServlet -> Interceptor -> Controller

여기서 주목해야 할 점은 Filter가 DispatcherServlet보다 더 앞단에서 동작한다는 것입니다. Spring Security는 인증 및 인가 등의 보안 관련 처리를 가장 앞단에서 수행하며, 이를 통해 컨트롤러로 불필요한 요청을 차단해서 보안성과 성능을 모두 확보할 수 있습니다.

특징

  • 모든 요청과 응답을 중앙 집중적으로 처리할 수 있습니다.
  • URL 패턴별로 적용이 가능합니다.
  • 여러 개의 필터를 Filter Chain으로 순차 적용이 가능합니다.
  • doFilter() 메서드에서 전/후 처리를 하고 다음 필터로의 제어가 가능합니다.

구조와 동작

public class CustomFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 요청 전 처리
        chain.doFilter(request, response); // 다음 필터로 요청 전달
        // 응답 후 처리
    }
}
  • init(): 필터 초기화
  • doFilter(): 실제 요청 및 응답을 처리
  • destroy(): 필터 종료

Filter vs. Interceptor

특징FilterInterceptor
관리 주체웹 컨테이너스프링 컨테이너
동작 위치DispatcherServlet 이전DispatcherServlet 이후
적용 대상모든 HTTP 요청 및 응답스프링 MVC Controller에 한정
용도인코딩, 인증 및 인가, 로깅, 보안 등인증 및 인가, 로깅, 권한 체크 등
객체 조작Request/Response를 직접 조작 가능직접 조작이 불가능, 데이터 가공 및 흐름 제어

간단하게 말하면 Filter는 저수준, 전역적, 스프링과 무관한 기능에 적합하며, Interceptor는 스프링 MVC와 연동되는 세부로직에 적합합니다.

Filter Interface

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이 호출됩니다.

등록 및 순서 지정

Spring Boot에서 Filter 등록 방법

@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

Filter의 동작 흐름 요약

  1. Filter Interface를 implements 하여 필터를 구현합니다.
  2. 구현한 Filter를 Bean으로 등록(FilterRegistrationBean 활용)합니다.
  3. HTTP 요청이 들어오면 등록된 순서대로 각 필터의 doFilter()가 호출됩니다.
  4. 모든 필터가 통과되면 Servlet이 실행됩니다.
  5. 필요시 init() 또는 destroy()로 Filter의 라이프사이클을 관리할 수 있습니다.

+@

  • ServletRequest는 기능이 제한적이므로 보통 HttpServletRequest로 다운캐스팅해 사용합니다.
  • @WebFilter annotation으로도 필터 등록이 가능하지만, 순서 조정이 어려워 FilterRegistrationBean의 사용을 권장합니다.

자료 및 코드 출처: 스파르타 코딩클럽

0개의 댓글