0+ 스프링/0+ 스프링 MVC

[스프링 MVC] 서블릿 필터를 어떻게 스프링 빈으로 관리할 수 있을까?

힘들면힘을내는쿼카 2023. 5. 25. 17:55
728x90
반응형

[스프링 MVC] 서블릿 필터를 어떻게 스프링 빈으로 관리할 수 있을까?

 

 

서블릿 필터는 서블릿 컨테이너 영역입니다.

그런데 우리는 서블릿 필터 구현체를 작성하고 빈으로 등록할 수 있습니다.

 

필터 인터페이스

public interface Filter {
    public default void init(FilterConfig filterConfig) throws ServletException {}
    public void doFilter(ServletRequest request, ServletRpesponse response, FilterChain chain) throws IOException, SevletException;
    public default void destory() {}
}

 

필터 인터페이스를 구현하고 빈(Bean)으로 등록하면
서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고, 관리
할 수 있습니다.



서블릿 필터는 서블릿 컨테이너의 영역에 속하지만,
스프링은 서블릿 컨테이너의 기능을 통해 필터를 관리할 수 있도록 지원합니다.!!!

 

어떻게 이런일이 가능 할까요? 🤔

 

먼저 보는 결론

  • DelegatingFilterProxy 이전: 서블릿 필터는 스프링 컨테이너에서 관리할 수 없음
  • DelegatingFilterProxy 이후: DelegatingFilterProxy를 통해 스프링 빈으로 필터를 등록할 수 있음
  • 스프링 부트 등장 이후: 내장 톰캣을 사용하는 스프링 부트의 등장으로 DelegatingFilterProxy를 거치치 않고 바로 필터를 등록할 수 있음

 

 

서블릿 필터가 뭔데?

먼저 서블릿 필터가 뭔지 모를 수 있습니다.
Client로 부터 Server로 요청이 들어오기 전에 서블릿을 거쳐서 필터링 하는 것을 서블릿 필터라고 합니다.

 

스프링으로 개발한 웹 애플리케이션에 HTTP요청을 하면,
웹 애플리케이션은 어떤 과정을 통해서 이를 처리할까요?

 

흐름

HTTP 요청 -> WAS -> 필터 -> 디스패처 서블릿 -> 컨트롤러

 

흐름을 보면 스프링 컨테이너로 요청이 오기전에 서블릿 컨테이너에서 먼저 요청을 처리하는 것을 알 수 있습니다.

Client로 부터 Server로 요청이 들어오기 전에 서블릿을 거쳐서 필터링 하는 것을 서블릿 필터라고 합니다.

 

공통적인 기능들을 서블릿이 호출되기 전에 수행(전처리)되게 하고 싶거나
서블릿이 호출 되고 난 뒤에 수행(후처리) 하고 싶으면 공통적인 기능들을 서블릿 필터로 구현하면 됩니다.

HTTP 요청 -> WAS -> 필터(요청 판단) -> 디스패처 서블릿 -> 컨트롤러

 

정리

서블릿이 호출되기 전, 후에 공통적인 기능(웹 공통 로직)을 추가할 때 사용한다.!

 

 

참고

  • 필터는 체인으로 구성되는데, 중간에 필터를 자유롭게 추가할 수 있습니다.

 

 

아니 어떻게 서블릿 컨테이너에 있는 것을 스프링 컨테이너에서 관리할 수 있는거야? 🤔

상식적으로 서블릿 컨테이너의 필터가 스프링 컨테이너에서 관리 될 수 있다는 것은 이상합니다. 🫨
하지만, 위처럼 필터를 구현하면 스프링 빈으로 등록 가능하다는 것을 알 수 있습니다.

 

옛날에는 실제로 필터가 스프링 컨테이너에 의해 관리될 수 없었습니다.
그런데 DelegatingFilterProxySpringBoot 가 등장하면서 얘기가 달라졌습니다.

 

DelegatingFilterProxy 두두등장!

필터에도 DI와 같은 스프링 기술이 필요한 상황이 발생하면서,
등장한 기술이 바로 DelegatingFilterProxy 입니다.

 

DelegatingFilterProxy서블릿 컨테이너에서 관리되는 프록시용 필터이고,
우리가 커스텀한 필터를 가지고 있습니다.

 

우리가 커스텀한 필터는 스프링 컨테이너에서 관리되는데,
요청이 오면 DelegatingFilterProxy가 요청을 받아서 우리가 커스텀한 필터에 요청을 위임합니다.

1. Filter 구현체(커스텀한 필터)가 스프링 빈으로 등록
2. SeveletContext가 Filter 구현체를 갖는 DelegatingFilterProxy를 생성
3. SeveletContext가 DelegatingFilterProxy를 서블릿 컨테이너에 필터로 등록
4. 요청이 오면 DelegatingFilterProxy가 필터 구현체에게 요청을 위임하여 필터 처리를 진행

 

스프링 부트를 사용하면 내장 톰캣을 지원하기 때문에
톰캣과 같은 서블릿 컨테이너까지 스프링 부트가 제어 가능합니다.

 

따라서 ServletContext에 우리가 커스텀한 필터 빈을 DelegatingFilterProxy로 감싸서 등록하지 않아도 됩니다!!
스프링 부트가 서블릿 필터의 구현체 빈을 찾으면 DelegatingFilterProxy 없이 바로 필터 체인에 필터를 등록해주기 때문입니다.^^👍

 

간단하게 테스트 해보자..!


CustomFilter

@Component
public class CustomFilter implements Filter {

    // 필터 초기화 메소드, 서블릿 컨테이너가 생성될 때 호출
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    // 요청을 실행하는 메소드, 필터가 여러개면 다음 필터를 호출한다.
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("["+now+"] 너 정말로 DelegatingFilterProxy 에 등록안해줘도 되는거니?");
        chain.doFilter(request, response);
    }

    // 필터 종료 메소드, 서블릿 컨테이너가 종료될 때 호출
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

 

TestController

@RestController
public class TestController {

    @GetMapping("/")
    public String test() {
        LocalDateTime now = LocalDateTime.now();
        return now.toString();
    }
}

 

결과

 

 

결과(시간)를 보면 CustomFilter가 먼저 요청을 받은 것을 알 수 있습니다.^^

 

참고

Spring 필터(Filter)가 스프링 빈 등록과 주입이 가능한 이유(DelegatingFilterProxy의 등장) - (2) - MangKyu’s Diary

 

 

 

 

728x90
반응형