이전 글: 2022.02.10 - [개발/spring] - [개념] interceptor vs filter 그리고 ContentCachingRequestWrapper
이전에 filter와 interceptor의 차이를 보았는데, 오늘은 filter와 oncePerRequestFilter의 차이를 보려고 한다. 그런데 이게 필턴데 스프링 컨텍스트 안에서 작용한다고? 그럼 또 interceptor를 놓칠 수 없다..
OncePerRequestFilter
OncePerRequestFilter는 Spring Framework에서 제공하는 필터로, Spring의 서블릿 컨테이너에서 동작한다. 이는 Spring의 일반적인 필터 체인에서 동작하는데, Spring이 제공하는 기능이기 때문에 Spring 이외의 서블릿 컨테이너에서는 자동으로 동작하지 않는다. 또한 extend로 필터를 구현하며 @Component로 등록되어 있어야 한다.
A Filter can be called either before or after servlet execution. When a request is dispatched to a servlet, the RequestDispatcher may forward it to another servlet. There's a possibility that the other servlet also has the same filter. In such scenarios, the same filter gets invoked multiple times.
Spring guarantees that the OncePerRequestFilter is executed only once for a given request.
spring은 filter에서 spring config 설정 정보를 쉽게 처리하기 위한 GenericFilterBean을 제공한다.
사용자는 요청을 한 번 보냈지만 redirect나 내부적인 이슈로 한 요청이 여러 번의 요청을 낳게 될 수 있다. 이 경우 인증, 로깅 등 필터가 (의도치 않게) 중첩적으로 호출되는데 이를 방지하고 하는 필터가 OncePerRequestFilter이다. OncePerRequestFilter 역시 GenericFilterBean상속받아 구현되어 있다. 즉 스프링이 제어한다는 것이고, 스프링은 OncePerRequestFilter가 주어진 요청에 대해 단 한 번만 수행되는 것을 보장한다.
OncePerRequestFilter는 다음과 같이 동작합니다:
- 서블릿 컨테이너에서 Spring 필터 체인이 실행될 때, OncePerRequestFilter는 요청을 가로채서 처리
- 필터 체인을 통해 요청을 전달할 수 있으며, 필터 체인 내에서 다른 필터들이 중복 실행되지 않도록 보장
- 필터 체인에서 중복 실행 방지가 필요한 경우에 사용. 일반적인 javax.servlet.Filter는 요청이 FORWARD 또는 INCLUDE로 서블릿 내부에서 재처리될 때 여러 번 호출될 수 있지만, OncePerRequestFilter는 각 요청당 한 번만 실행됩니다.
실제 동작 흐름:
아무 설정을 하지 않아도 Spring Boot는 Spring 필터(Specifically, Spring Security 등)를 서블릿 컨테이너 필터보다 먼저 실행되도록 자동으로 설정합니다. 이는 Spring Boot의 자동 구성(Autoconfiguration) 기능과 내부 필터 체인 관리 방식 덕분입니다. Spring Boot는 애플리케이션 시작 시 자동으로 Spring 필터들을 서블릿 컨테이너에서 관리되는 필터보다 우선적으로 실행되도록 설정합니다.
- 서블릿 컨테이너가 요청을 받습니다.
- Spring Boot는 Spring 필터 체인을 먼저 실행하도록 설정합니다.
- Spring에서 관리하는 필터(예: OncePerRequestFilter)가 먼저 실행됩니다.
- 그 후, 서블릿 컨테이너에 등록된 일반 필터(javax.servlet.Filter)가 실행됩니다.
once-filter -> filter -> interceptor -> controller -> interceptor -> filter -> once-filter
2024-10-08 15:35:41 INFO [c.n.a.f.CustomServletWrappingFilter .doFilterInternal : 24] [once-filter][REQUEST] [GET] /api/asdf
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestLogFilter .doFilter : 34] [filter][REQUEST] [GET] /api/asdf
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestInterceptor .preHandle : 21] [interceptor][REQUEST] [GET] /api/asdf
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestInterceptor .postHandle : 39] [interceptor][RESPONSE] [GET] /api/asdf 404 - 0.003 ms
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestLogFilter .doFilter : 43] [filter][RESPONSE] [GET] /api/asdf 404 - 0.01ms
2024-10-08 15:35:41 INFO [c.n.a.f.CustomServletWrappingFilter .doFilterInternal : 32] [once-filter][RESPONSE] [GET] /api/asdf 404 - 0.011ms
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestInterceptor .preHandle : 21] [interceptor][REQUEST] [GET] /error
2024-10-08 15:35:41 INFO [c.n.aapoker.filter.RequestInterceptor .postHandle : 39] [interceptor][RESPONSE] [GET] /error 404 - 0.013 ms
에러 화면을 요청할 경우 필터와 onceRequestFilter는 한번씩만 실행되고, 인터셉터는 스프링 진영에서 자동으로 route되는 error화면까지 잡힌다.
onceRequestFilter도 스프링에서 관리되는 필터지만 error를 잡지 않는다는 것을 확인!
참고) ContentCachingRequestWrapper
ContentCachingRequestWrapper는 요청(Request) 본문을 캐싱할 수 있도록 도와주는 래퍼 클래스입니다. 기본적으로, 서블릿 요청의 본문은 한 번만 읽을 수 있는 스트림 형태로 제공됩니다. 하지만 특정 상황에서는 요청 본문을 여러 번 읽어야 하거나, 로깅 또는 분석 목적으로 요청 본문을 캐싱하고자 할 때가 있습니다.
ContentCachingRequestWrapper의 역할:
- 요청 본문을 메모리에 캐싱하여, 한 번 이상 읽을 수 있도록 지원.
- 요청 본문을 나중에 로그로 남기거나, 요청 데이터를 분석할 때 유용.
'개발 > spring' 카테고리의 다른 글
[spring] ChainedTransactionManager deprecated from boot 2.5 (0) | 2022.03.16 |
---|---|
[logback] Reflective setAccessible(true) disabled (0) | 2022.03.16 |
[annotation] NotNull NotBlank NonNull NotEmpty... (0) | 2022.02.11 |
[개념] interceptor vs filter 그리고 ContentCachingRequestWrapper (0) | 2022.02.10 |
[retry] spring-retry test code (1) | 2022.02.07 |