Language & Framework/Spring Boot

[Spring Boot] Spring의 전체 흐름 알아보기 - 02. Filter

코딩 기록하는 애기 개발자 2026. 4. 15. 01:55

목차

     

    이전 글에서는 Spring MVC의 전체적인 요청 처리 흐름을 중심으로,
    요청이 어떻게 Controller까지 전달되고, 어떤 과정을 거쳐 응답이 생성되는지에 대해 살펴보았다.

     

    하지만 흐름을 따라가다 보니

    Spring MVC와 함께 자주 듣게 되는 Filter에 대해서도 공부해봐야겠다는 생각이 들었다.

     

    또한 이전에 Figma로 그려본 Spring MVC 구조를 기준으로,

    이 요소가 실제로 어느 위치에서 동작하는지도 함께 정리해보고 싶었다.


    Filter 란 ?

    Servlet에 도달하기 전, 요청을 가로채 로직을 수행하는 객체를 말한다. 

     

    서버로 요청이 들어오는 경우, `Dispatcher Servlet`을 거치기 전에 요청 사항에 대한 공통 관심사를 처리하기 위한 필터 로직을 수행한다. 대표적으로 `Spring Security`를 활용하여 요청에 대한 필터 기반 인증, 인가 처리를 수행하기 위해 주로 많이 사용된다. 

     

    public interface Filter {

     

    Filter는 Servlet 또는 정적 리소스에 대한 요청이나 응답을 중간에서 가로채서 필요한 처리를 수행하는 객체이다. 
    즉, 요청이 Controller까지 들어가기 전이나, 응답이 클라이언트로 나가기 전에 공통적인 작업을 수행할 수 있는 역할을 한다. 

    이러한 실제 필터링 로직은 `doFilter()`메서드에서 이루어지며, 인증, 로깅 및 데이터 변환과 같은 공통 기능들이 주로 이곳에서 처리된다. 또한, ``FilterConfig``를  통해 초기화 설정 값을 가져오거나 ``ServletContext``에 접근하여 필요한 리소스를 사용할 수도 있다.

    이처럼 Filter는 단순히 요청을 전달하는 것이 아니라, 웹 애플리케이션 전반에서 공통 관심사를 처리하기 위해 활용되는 중요한 컴포넌트이다. 

     

    ServletContext 란? 
    웹 애플리케이션 전체를 대표하는 객체로, 쉽게 말하면 서버 안에서 모든 요청이 함께 공유하는 공간이라고 생각하면 된다.
    Tomcat 같은 웹 컨테이너가 애플리케이션을 처음 실행할 때 딱 한 번 생성되며, 애플리케이션이 종료될 때까지 하나만 존재한다. 

    이 객체를 통해 모든 서블릿이나 필터는 공통된 데이터를 저장하거나 가져다 사용할 수 있다. 
    >> React 의 Context API 와 굉장히 비슷한 것 같다 !! 

     

     

    그럼 Filter는 어떻게 동작할까 ?

    DispatcherServlet은 Spring의 가장 앞단에 존재하는 Front Controller이므로 Filter는 Spring 밖에서 처리가 된다.
    즉, Spring Container가 아닌 Tomcat과 같은 Web Container 에 의해 관리가 되는 것이고, DispatcherServlet 전/후 에 처리하는 것이다. 
    1. 클라이언트가 서버에 HTTP 요청을 보내면, 서버에 도착한 요청은 먼저 Filter를 거치게 된다.
    2. Filter는 요청을 가로채 여러가지 작업을 수행한다.
      이러한 Filter의 동작 흐름은 상황에 따라 다음과 같이 나눌 수 있다.
      • 올바른 요청인 경우 : 작업을 수행한 후 DispatcherServlet으로 전달한다.
      • 유효하지 않은 요청인 경우 : DispatcherServlet을 거치지 않고 Filter 내에서 유효하지 못한 요청에 대한 응답을 반환한다.
    3. DispatcherServlet은 전달받은 요청을 기반으로 이후의 과정을 수행한다. 

     

    Filter가 여러 개인 경우에는 어떻게 동작할까 ? 

    위 그림을 보면 Filter Chain 이라는 개념과 함께 여러 개의 Filter가 하나의 흐름으로 묶여있는 것을 확인할 수 있다. 

     

    Filter는 하나만 동작하는 것이 아니라, 여러 개가 순서대로 연결된 Chain 구조로 동작한다.

    클라이언트의 요청이 들어오면 첫번째 Filter부터 순차적으로 실행되며, 각 Filter는 FilterChain을 통해 다음 Filter로 요청을 전달한다.

     

    이러한 과정을 거쳐 마지막에는 DispatcherServlet까지 요청이 전달된다. 

     

    실제 코드를 실행했을 때의 결과

     

    실제 코드를 디버깅 했을 때, 위와 같이 Filter들이 체인으로 구성된 것을 확인할 수 있다. 

     

     

    실제로 직접 만든 Filter 코드 내부에서 `doFilter(request, response)` 메서드가 호출되는 것을 볼 수 있는데, 
    이 코드는 현재 Filter에서 처리를 마친 뒤, 다음 Filter로 요청과 응답을 넘기는 역할을 한다. 

     

     

    Filter의 주요 메서드 

    Filter는 위에서 설명했듯이 클라이언트의 요청을 가로채 다양한 작업을 수행한다.
    이러한 Filter의 동작은 정해진 생명주기와 주요 메서드를 통해 이루어진다.

     

    01 ``init()``

    필터 객체를 초기화하고 서비스에 등록하기 위한 메서드이다. 

    필터가 생성될 때 한 번 번 호출되며, 초기화 이후 들어오는 모든 요청은 `doFilter()` 메서드를 통해 처리된다. 

     

    default void init(FilterConfig filterConfig) throws ServletException { }

     

    Tomcat과 같은 서버가 Filter 객체를 생성한 뒤, 해당 Filter가 정상적으로 동작할수 있도록 준비시키기 위해 위 `init()` 메서드를 딱 한 번 호출한다. 이 과정은 Filter가 실제 요청을 처리하기 전에 반드시 먼저 완료되어야 하는 초기화 단계이다.

    만약 `init()` 메서드 실행 중에 ``ServletException``이 발생하거나,
    웹 컨테이너가 정해둔 시간 안에 메서드가 끝나지 않으면 해당 Filter는 정상적으로 서비스에 등록되지 못한다. 

     

    기본적으로 `init()`은 아무 작업도 하지 않는 빈 구현 상태로 제공되며, 
    개발자는 필요할 때만 이 메서드를 오버라이드해서 초기 설정 로직을 작성하면 된다.

     

    또한, ``filterConfig``를 통해 해당 Filter의 설정 정보나 초기화 파라미터를 가져올 수 있다. 

     

     

    02 ``doFilter()``

    클라이언트의 요청 및 응답을 처리할 때 실행되는 메서드이다.

     

    요청이 들어오면 이를 가로채 인증, 인가, 로깅 등을 수행할 수 있으며,
    `FilterChain`을 통해 다음 Filter 또는 DispatcherServlet으로 요청을 전달한다. 

     

    또한, 조건에 따라 요청을 전달하지 않고 해당 메서드 내에서 바로 응답을 반환할 수도 있다. 

     

      void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException;

     

    `doFilter()` 메서드는 클라이언트의 요청과 응답이 Filter를 통과할 때마다 웹 컨테이너에 의해 호출되는 핵심 메서드이다. 

    이 메서드 안에서는 Filter를 들어온 요청을 검사하거나, 필요에 따라 요청과 응답 객체를 감싸서 내용을 수정할 수 있다.

     

    이후 ``FilterChain``을 통해 다음 Filter 또는 DispatcherServlet으로 요청을 전달할 수 있으며,
    반대로 `chain.doFilter()`를 호출하지 않으면 요청을 다음 단계로 넘기지 않고 여기서 차단할 수도 있다. 

     

    즉, `doFilter()`는 요청을 전처리하고, 다음 단계로 전달하며, 후처리까지 하는 전체 흐름을 제어하는 역할을 한다. 

     

     

    03 ``destroy()``

    필터 종료 메서드로 서블릿 컨테이너가 종료될 때, 즉 필터 인스턴스가 제거될 때 호출되는 메서드이다.  

    public default void destroy() {}

     

    `destroy()` 메서드는 웹 컨테이너가 Filter를 더 이상 사용하지 않을 때, 즉 서비스에서 제거될 때 호출되는 메서드이다. 

    이 메서드는 Filter 내부에서 실행중이던 모든 `doFilter()` 작업이 끝난 이후에 단 한 번만 실행되며, 이후에는 해당 Filter 인스턴스의 `doFilter()`는 다시 호출되지 않는다.

     

    주로 이 시점에는 Filter가 사용하는 자원들을 정리하거나, 필요한 상태를 저장하는 작업을 수행한다.

    다만 기본 구현은 `init()`과 마찬가지로 아무 동작도 하지 않는 빈 구현으로 제공되기 때문에

    특별히 정리할 자원이 없는 경우에는 별도로 구현하지 않아도 된다. 

     

     

     

     

    위 코드들을 보면 알다시피, 별도로 구현하지 않아도 되는 메서드인 `init()`과 `destroy()`에는 ``default`` 키워드가 붙어있다. 이는 해당 메서드에 이미 기본 구현이 포함되어 있음을 의미하며, 필요한 경우에만 오버라이드하여 사용할 수 있다. 따라서 개발자는 해당 메서드를 직접 구현하지 않더라도 컴파일이나 실행에는 문제가 없으며, 실제로는 필요한 경우에만 선택적으로 구현하면 된다. 

     

     


     

    지금까지 Filter에 대해서 알아보았다.

    다음 글에서는 Filter와 비슷한 역할을 하지만, Spring 내부에서 동작하는 ``Interceptor``에 대해 살펴보며
    두 개념의 차이와 각각 언제 사용하는 것이 적절한지에 대해 정리해보고자 한다. 

     

    Spring ..............

    너 정말 파면 팔 수록 끝이 보이지 않는군아 ...... 💦