[Spring] Filter 흐름제어는 어디서할까?

전영호·2024년 10월 16일

Spring

목록 보기
1/3

스프링 내부에서 필터가 어떻게 동작하는지 궁금해서 직접 디버깅을 걸고 확인해 보았다.

🔥 결론

ApplicationFilterChain 에서 필터들에 대한 흐름제어를 한다.

ApplicationFilterChain 내부에는 Filter 들에대한 배열이 존재한다. 배열을 순차적으로 따라가면서 filter.doFilter 를 호출한다. 그리고, 그 필터의 동작이 끝났으면 ApplicationFilterChain의 doFilter를 호출 한다.
반복문 형태가 아니라 재귀적으로 호출을 이어나가는 형태이다.

화살표로 간단히 흐름을 표시해 보았다.

전체 도식을 그려보았다.

🔍 동작확인

눈으로 직접 확인을 해야 맘이 편하므로 직접 확인해보자.
Spring Boot 3.3.4 기준 Spring Web 에 대한 의존성만을 넣고 디버깅해보았다.

filters[] 배열에 4개의 필터가 들어가 있다.
실제로 서블릿이 호출되기 까지의 과정을 모두 트레이싱 해보면

위에서 본 4개의 필터만이 호출되었음을 알 수 있다.

그런데, 그 외에 OncePerRequestFilter 객체도 계속 보이게 된다.
사실 4개의 필터 모두 OncePerRequestFilter 라는 abstract class 를 상속하는 클래스이다.
스프링에서는 필터가 단 한번 사용되는 것을 보장하기 위해 OncePerRequestFilter 를 상속해서 필터를 사용한다.

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (request instanceof HttpServletRequest httpRequest) {
            if (response instanceof HttpServletResponse httpResponse) {
                String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
                boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
                if (!this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
                    if (hasAlreadyFilteredAttribute) {
                        if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
                            this.doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
                            return;
                        }

                        filterChain.doFilter(request, response);
                    } else {
                        request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

                        try {
                        // 중요한 부분
                            this.doFilterInternal(httpRequest, httpResponse, filterChain);
                        } finally {
                            request.removeAttribute(alreadyFilteredAttributeName);
                        }
                    }
                } else {
                    filterChain.doFilter(request, response);
                }

                return;
            }
        }

        throw new ServletException("OncePerRequestFilter only supports HTTP requests");
    }
    
   
   protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException;

OncePerRequestFilter 의 doFilter 코드는 위와 같다. final 로 선언되어서 자식 클래스에서 상속할 수 없도록 막아두었다. //중요한 부분 이라고 해놓은 부분에서 볼 수 있듯이 자식 필터들에서는 doFilterInternal 을 상속받아서 그곳에서 로직을 작성해주면 된다.

🎃 스프링 시큐리티 의존성을 추가하면 어떻게 될까?

시큐리티 공식 문서에 나오는 도식을 보고 궁금해져서 직접 확인해보았다.

실제로 확인해보니 DelegatingFilterProxyRegistrationBean 이 filter 목록에 추가되었다.

0개의 댓글