Spring Boot

[Spring Boot] Filter를 왜 사용해야하고 어떻게 사용하는 걸까?

kkkkkkkkkkkk 2022. 7. 30. 02:39

Servlet 에서 제공하는 기능이자 스프링 프레임워크에서도 제공하는 기능이다. 말 그대로 앞단에서 무언가를 걸러줄 때 사용한다.

 

아래 그림은 대표적인 구성도다.

 

 

DisPatherServlet 앞단에서 존재하고 있고 DisPatherServlet이 요청과 응답을 다루기 전과 후에 동작을 한다.

 

위 그림만 보면 라이프 사이클이 예상 될 것이다.

클라이언트로부터 오는 모든 요청과 응답의 순수한 정보에 대해서 컨트롤 할 수 있다는 것을 기대한다.

특징

  1. 순수한 RequestBody와 ResponseBody를 확인을 할 수 있다.
  2. 유일하게 servletRequest, servletResponse의 객체를 변환 할 수 있다.
  3. 인증과 관련된 로직들을 해당 필터에서 처리한다.

 

왜 Filter 사용해야할까?

요청으로 들어온 정보에 노출되면 위험한 정보들이 들어 있을 수 있다.

이를 DispatcherServlet 이 처리하기 전에 filter에서 인코딩을 해주는 식과 인증, 인가 처리를 해야한다.

 

아직 security를 공부하지 않아 인증과 인가 관련해서 지식이 부족하다. 그러므로 filter의 사용 예시는 잘 모르겠다. security 를 배울 때 Filter를 적용하여 포스팅을 다시 해보겠다.

 

필터의 라이프 사이클

인터페이스 FIlter를 직접 구현하여 메서드를 오버라이딩 해보자.

 

필터가 동작하기 원한다면 @Component 를 붙여 bean으로 등록해야한다.

 

제일 먼저 실행되는 메서드는 init() 메서드가 호출 될 것이다.

다음으로 doFilter() 메서드가 호출되는데 doFilter() 는 매개변수로 ServletRequestServletResponse 를 받는 것에 주목하자.

 

우리는 이것을 가지고 데이터를 받아 볼 수 있다는 것이다.

 

다음으로 destroy() 메서드는 시스템이 종료 될 때 호출된다.

 

 

로그 그림을 보면 기대한것과 같게 호출 될 것이다.

 

필터 적용법

  1. @ServletComponentScan + @WebFilter

@SpringBootApplication 이 붙은 클래스에 @ServletComponentScan 을 선언한다.

더이상 FIlter를 구현한 곳에 @Component 를 선언하지 않아도 된다.

 

하지만 @WebFilter 라는 에노테이션을 선언 해줘야 한다.

 

 

Spring의 ComponentScan 원리와 같은 원리로 동작이 된다. @ServletComponentScan@WebFilter

를 선언한 클래스를 읽어들여 Filter 등록을 해준다.

다음으로 urlPatterns 옵션에 필터를 적용 할 url를 넣어준다.

 

  1. @Bean 등록 + FilterRegistrationBean 등록

 

여러개의 Filter 를 등록 할 경우 bean으로 등록하여 사용 할 수 있고 setOrder() 으로 순서를 정할 수 있다. 매개변수 타입은 int 다.

다음으로 setUrlPatterns() 으로 특정 url 에 대해 필터를 적용할 수 있다. 매개변수 타입은 List<String> 이다

 

init() 호출은 setOrder() 과는 무관하게 호출이 되는 것 같다.

 

 

doFilter() 호출은 setOrder() 을 설정한 대로 호출이 된다.

 

 

destroy() 호출도 setOrder() 과는 무관하게 호출이 된다.

 

 

필터에 들어온 요청과 응답의 정보 Logging 하기 (전 처리 / 후 처리)

 

ServletRequest와 ServletResponse가 매개변수로 들어오면 ContentCachingRequestWrapperContentCachingResponseWrapper 에 의존성 주입 시켜 인스턴스를 생성한다.

 

ContentCachingRequestWrapper / ContentCachingResponseWrapper 를 사용하는 이유는 요청 응답에 대한 정보를 바이트 배열로 담아 필요할 때 꺼내 쓰기 위한 것이다.

 

다음으로 chain.doFilter(httpRequest, httpResponse) 을 호출 하고 후 처리 과정에서 요청 응답 정보를 담아 logging 작업을 한다.

 

반드시 후 처리 과정에서 정보를 가져와 logging 해야 한다.

 

ContentCachingRequestWrapper / ContentCachingResponseWrapper 는 인스턴스 생성 시점에 길이만 초기화되고 요청 응답 정보는 읽어 오질 않는다.

 

실제로 디버깅을 해보면 chain.doFilter() 가 호출이 된 후에 요청 응답 정보를 읽어온다.

 

 

이제 동작을 시켜 loggig 작업이 잘 되는지 확인하자

 

 

그런데 로깅은 잘 나오는데 응답 메시지가 내려오질 않는다.

 

 

ContentCachingRequestWrapper / ContentCachingResponseWrapper 는 내부에 Stream 기반 클래스들을 참조하여 동작하고 있다. Stream은 일회성이기 때문에 따로 바이트 배열을 복사를 해야한다.

 

 

ContentCachingResponseWrapper 는 복사 작업의 메서드를 제공하고 있다.

 

응답이 잘 나온것을 볼 수 있다.