이번 포스트에서는 Spring Filter에 대해서 알아보도록 하자
1. Spring에서 Filter의 개념
필터의 정의
- 필터는 웹 애플리케이션에서 클라이언트의 요청과 서버의 응답을 가로채는 재사용 가능한 코드 조각이다.
- 서블릿 스펙의 일부로, 요청이 서블릿에 도달하기 전과 응답이 클라이언트로 반환되기 전에 특정 작업을 수행할 수 있다.
서블릿 필터의 기본 원리
- 필터 체인을 통해 여러 필터를 순차적으로 적용할 수 있다.
- 각 필터는 doFilter() 메서드를 통해 요청과 응답을 조작하고, 필터 체인의 다음 필터나 최종 목적지(서블릿 또는 정적 리소스)로 요청/응답을 전달할 수 있다.
스프링 부트에서 필터의 역할
- 스프링 부트는 자동 구성을 통해 필터를 쉽게 추가하고 관리할 수 있게 해준다.
- 보안, 트랜잭션 관리, 로깅 등 다양한 크로스커팅 관심사(Cross-cutting Concerns)를 처리하는 데 필터(Filter)를 사용한다.
요청 처리 과정에서 필터의 위치와 흐름
- 아래 다이어그램과 같은 방식으로 필터가 동작한다.
필터의 처리 흐름 설명
클라이언트에서 서버로 요청:
- 클라이언트가 서버에 HTTP 요청을 보내면, 서버에 도착한 요청은 먼저 필터(Filter)를 거치게 된다.
필터 처리:
- 필터는 요청을 가로채서 여러 가지 작업을 수행할 수 있다. 예를 들어, 인증 작업을 하거나, 로깅, 요청 내용 변경 등을 할 수 있다.
- 필터는 여러 개가 연쇄적으로 구성될 수 있어서, 하나의 요청이 여러 필터를 거칠 수도 있다.
DispatcherServlet으로 전달:
- 필터를 통과한 요청은 스프링의 핵심인 DispatcherServlet으로 전달된다. DispatcherServlet은 요청을 적절한 서블릿이나 컨트롤러로 라우팅 하는 역할을 한다.
서블릿/컨트롤러에서 처리:
- 요청은 이제 서블릿이나 컨트롤러에 도달해서 실제 비즈니스 로직이나 데이터 처리가 이루어진다. 이 과정에서 데이터베이스 조회, 데이터 가공 등이 수행될 수 있다.
응답의 필터 처리:
- 서블릿이나 컨트롤러에서 처리된 응답은 다시 DispatcherServlet을 거쳐 필터로 전달된다. 필터는 이 응답에 대해서도 가공을 할 수 있다. 예를 들어, 응답 데이터를 압축하거나, 특정 헤더를 추가하는 등의 작업이 있을 수 있다.
클라이언트로 응답 반환:
- 마지막으로 필터를 거친 응답이 클라이언트에게 반환되면서, 클라이언트는 요청한 작업의 결과를 받게 되는 것이다.
이 과정을 통해 서버는 클라이언트의 요청을 안전하고 효율적으로 처리할 수 있고, 필터를 통해 요청과 응답을 유연하게 조작할 수 있다. 이런 구조 덕분에 스프링 애플리케이션은 확장성과 유지보수성이 높아진다.
2. 필터(Filter) vs 인터셉터(Interceptor)
필터(Filter)
- 먼저, 필터에 대해서 설명하자면, 이건 서블릿 컨테이너 레벨에서 작동한다. 즉, 애플리케이션으로 들어오는 모든 요청과 응답에 대해
실행된다는 것이다. 이 말은 정적 자원 요청에도 필터가 적용된다는 의미다. - 필터는 주로 web.xml 파일이나 서블릿 초기화 코드를 통해 설정된다. 실행 시점은 요청이 DispatcherServlet에 도달하기 전과 후, 그리고 서블릿이나 JSP가 실행되기 전후에 작동한다.
인터셉터(Interceptor)
- 빈먄 인터셉터는 좀 다른 방식으로 작동한다. 인터셉터는 스프링 콘텍스트 내에서 동작하고, 스프링의 DispatcherServlet을 통해 처리되는 요청에만 적용된다.
- 인터셉터를 설정하는 방법은 스프링의 설정 파일을 사용하는 건데, 예를 들어 WebMvcConfigurer 구현체를 통해서 설정할 수 있다. 인터셉터의 실행 시점은 DispatcherServlet이 컨트롤러를 호출하기 전과 후, 그리고 응답이 뷰에 의해 렌더링된 후이다.
필터와 인터셉터의 차이점
- 이제 필터와 인터셉터의 차이점을 살펴보자. 가장 큰 차이는 작동 범위다. 필터는 서블릿 컨테이너 레벨에서 모든 요청과 응답에 대해 작동하는 반면, 인터셉터는 스프링 MVC 레벨에서 DispatcherServlet을 통해 처리되는 요청에만 적용된다.
- 또한, 필터는 스프링 콘텍스트와는 독립적으로 작동하지만, 인터셉터는 스프링의 빈과 통합되어 스프링의 기능들을 활용할 수 있다. 그리고 실행 시점에 있어서도 차이가 있다. 필터는 주로 서블릿이나 JSP가 실행되기 전후에만 작동하지만, 인터셉터는 컨트롤러의 메서드 호출 전후와 뷰가 렌더링된 후에도 작동할 수 있다.
그림으로 이해하는 필터와 인터셉터의 동작
- 이 다이어그램은 HTTP 요청이 필터를 거쳐 서블릿 컨테이너로 들어가고, 거기서 DispatcherServlet을 통해 인터셉터로 이동한 다음 컨트롤러로 전달되는 과정을 보여준다. 응답은 이 경로를 역순으로 따라가며 다시 필터를 거쳐 HTTP 응답으로 클라이언트에게 돌아간다.
3. Spring MVC에서 필터와 인터셉터의 실행 시점 이해하기
스프링의 DispatcherServlet과 필터,인터셉터의 실행 시점을 이해하기 위한 다이어그램
- 이 시퀀스 다이어그램은 필터와 인터셉터의 실행 시점과 스프링 MVC의 DispatcherServlet과의 관계를 보여준다.
- 필터가 모든 요청에 대해 독립적으로 작동하는 반면, 인터셉터는 스프링 MVC의 DispatcherServlet과 밀접하게 연관되어 있음을 강조한다.
필터(Filter)
- 클라이언트로부터의 요청이 서블릿 컨테이너에 도달하면 가장 먼저 실행된다.
- 필터는 DispatcherServlet과 독립적으로 작동하여 요청과 응답을 처리한다.
인터셉터(Interceptor)
- DispatcherServlet이 컨트롤러를 호출하기 직전(Pre-Controller), 직후, 그리고 응답 전(Post-Controller)에 실행된다.
- 인터셉터는 DispatcherServlet의 일부로서 작동하며, 컨트롤러 호출 과정에서 요청을 가로채고 처리한다.
시퀀스 설명
- 클라이언트에서 요청 시작: 클라이언트가 서버로 HTTP 요청을 보내면
- 필터 동작: 요청은 먼저 필터를 통과한다. 필터는 서블릿 컨테이너 레벨에서 작동해서 요청을 처리하거나 변형시킬 수 있다.
- DispatcherServlet으로 이동: 필터를 통과한 요청은 스프링의 DispatcherServlet으로 전달된다.
- 인터셉터 동작: DispatcherServlet은 요청을 컨트롤러로 보내기 전에 인터셉터를 거치게 한다. 인터셉터는 요청을 가로채서 추가적인 처리를 할 수 있다.
- 컨트롤러에서 처리: 요청은 컨트롤러에 도달해서 비즈니스 로직에 따라 처리되고
- 응답의 인터셉터 처리: 처리된 응답은 다시 인터셉터를 거쳐서 DispatcherServlet으로 돌아가고
- 필터를 통한 응답 처리: 그다음에 필터를 통과해서 최종적으로 클라이언트에게 전달된다.
간단히 말해서, 필터는 모든 요청과 응답에 대해 서블릿 레벨에서 독립적으로 작동하고, 인터셉터는 스프링 MVC 흐름 안에서 DispatcherServlet과 컨트롤러 사이에서 요청을 처리하는 역할을 한다.
시퀀스 설명을 이해하기 위해 알아야할 용어
톰캣(Tomcat)
- 웹 애플리케이션 서버(WAS)로, 자바 서블릿 컨테이너의 역할을 해. 웹 서버와 통신하면서 요청을 받고, 서블릿을 실행시켜 응답을 생성하는 역할을 한다. 즉, 톰캣은 HTTP 요청을 받아서 처리하고, 그 결과를 웹 브라우저에게 돌려주는 서버 소프트웨어이다.
DispatcherServlet
- 스프링 MVC 프레임워크의 일부로, 스프링이 제공하는 특별한 서블릿이야. 이 서블릿은 스프링 애플리케이션의 중앙 집중식 진입점으로 작동하며, 들어오는 요청을 알맞은 컨트롤러로 라우팅 하는 역할을 한다. 즉, DispatcherServlet은 스프링 MVC의 흐름을 관리하는 컨트롤 타워 같은 거라고 볼 수 있다.
스프링 MVC의 콘텍스트
- 스프링이 관리하는 애플리케이션의 실행 환경을 말해. 여기에는 컨트롤러, 서비스, 데이터 접근 객체 등 스프링이 관리하는 모든 빈(bean)들이 포함되어 있다. 이 콘텍스트는 스프링의 의존성 주입 기능을 통해 애플리케이션의 다양한 부분들이 서로 연결되고 협력할 수 있게 해준다.
요약하자면, 필터는 톰캣 같은 서블릿 컨테이너에서 처리하는 모든 요청에 대해 작동할 수 있고, 인터셉터는 스프링 MVC의 컨텍스트 안에서, DispatcherServlet이 처리하는 요청에 대해서만 작동한다.
4. 필터가 선호되는 상황1
필터가 선호되는 상황 1
- 필터는 서블릿 컨테이너 수준에서 요청과 응답을 처리해야 하는 상황에서 특히 유용하다. 이런 상황들은 스프링의 DispatcherServlet이 처리하기 전에 일어나야 하는 작업들이다.
- 예를 들어, 서버로 들어오는 요청의 압축을 해제하는 경우를 생각해보자. 클라이언트에서 압축된 데이터를 보내면, 서버에서는 이 데이터를 해제해야 한다. 이런 작업은 요청이 스프링의 DispatcherServlet에 도달하기 전에 처리되어야 하기 때문에 필터를 사용하는 것이 적절하다.
- 이렇게 서블릿 컨테이너 수준에서 요청이나 응답을 처리해야 할 때, 필터를 사용하는 것이 바람직하다. 필터를 통해 압축 해제, 보안 헤더 추가 등의 작업을 효율적으로 수행할 수 있고, 스프링의 웹 계층에 들어가기 전에 필요한 처리를 할 수 있어서이다.
이렇게 서블릿 컨테이너 수준에서 요청이나 응답을 처리해야 할 때, 필터를 사용하는 것이 바람직하다. 필터를 통해 압축 해제, 보안 헤더 추가 등의 작업을 효율적으로 수행할 수 있고, 스프링의 웹 계층에 들어가기 전(MVC의 DispatcherServlet이 관여하기 전)에 필요한 처리를 할 수 있어서이다.
다이어그램으로 과정 시각화
- 이 다이어그램은 클라이언트로부터 압축된 요청이 서버에 도착하는 순간부터 최종 응답이 클라이언트에게 돌아가기까지의 필터 작동 과정이다. 순서는 다음과 같다.
- 압축된 요청: 클라이언트가 압축된 데이터를 서버로 보낸다.
- 요청의 압축 해제: 필터가 이 요청을 가로채서 압축을 해제한다.
- DispatcherServlet으로 전달: 압축이 해제된 요청이 스프링의 DispatcherServlet으로 전달된다.
- 비즈니스 로직 처리: DispatcherServlet은 요청을 서비스 레이어로 전달해서 비즈니스 로직을 처리하게 한다.
- 보안 헤더 추가: 서비스 레이어에서 처리된 응답이 DispatcherServlet을 통해 다시 필터로 전달되기 전에 보안 헤더를 추가한다.
- 최종 응답: 필터를 통과한 최종 응답이 클라이언트에게 돌아간다.
이 과정을 통해서, 필터가 서블릿 컨테이너 수준에서 요청과 응답을 처리하는 중요한 역할을 한다는 걸 알 수 있다.
5. 필터가 선호되는 상황2
필터가 선호되는 상황2
- 이번에는 애플리케이션 전역에 걸친 로깅 및 보안 검사가 필요한 경우다.
- 애플리케이션 전역에서 일관된 보안 검사나 로깅이 필요한 상황을 생각해보자. 예를 들어, 모든 요청에 포함된 인증 토큰의 유효성을 검사해야 할 때가 있다. 이런 경우에는 각 요청이 스프링의 DispatcherServlet에 도달하기 전에 인증 토큰을 검증하는 것이 중요하다. 이때 필터를 사용하면 이런 유형의 보안 검사를 효과적으로 수행할 수 있다.
이렇게 애플리케이션 전역에서 필요한 로깅과 보안 검사는 필터를 사용하여 효과적으로 처리할 수 있다. 필터는 모든 요청에 대해 전역적으로 실행되기 때문에, 이러한 전역적인 작업을 수행하기 위한 이상적인 선택이 될 수 있다.
다이어그램으로 과정 시각화
- 이 다이어그램은 클라이언트로부터 요청이 서버에 도착하는 순간부터 최종 응답이 클라이언트에게 돌아가기까지의 필터 작동 과정을 보여준다.
- 인증 토큰 포함 요청: 클라이언트가 인증 토큰을 포함한 요청을 서버로 보낸다.
- 토큰 검증: 필터가 요청을 가로채서 인증 토큰의 유효성을 검사한다.
- 요청 로깅: 필터가 요청의 세부 사항을 로그에 기록한다.
- DispatcherServlet으로 전달: 유효한 요청이 스프링의 DispatcherServlet으로 전달된다.
- 비즈니스 로직 처리: DispatcherServlet은 요청을 서비스 레이어로 전달해서 비즈니스 로직을 처리하게 한다.
- 최종 응답: 처리된 응답이 DispatcherServlet을 통해 필터로 전달되고, 필터를 통과한 후 클라이언트에게 돌아간다.
이 과정을 통해, 필터가 애플리케이션 전역에 걸쳐 로깅과 보안 검사를 수행하는 데 있어 중요한 역할을 한다는 걸 알 수 있다.
6. 필터가 선호되는 상황3
필터가 선호되는 상황 3
- 필터가 선호되는 세 번째 상황에 대해 설명하겠다. 이 상황은 요청이나 응답 스트림을 직접 조작해야 할 때 발생한다.
- 가장 대표적인 예로, 서버로 전송된 GZIP으로 압축된 요청을 해제하는 경우를 들 수 있다. 클라이언트로부터 받은 GZIP으로 압축된 요청이 서버에 도착했을 때, 이를 해제하는 작업이 필요하다. 이런 경우, 필터를 사용하면 서블릿이 요청을 처리하기 전에 이 압축을 효과적으로 해제할 수 있다.
- 또 다른 예는 요청 본문에 포함된 JSON 데이터를 파싱하고 변환하는 작업이다. 예를 들어, 클라이언트가 보낸 JSON 데이터를 파싱해서 필요한 형태로 변환해야 할 때가 있다. 이런 경우에도 필터는 요청 스트림을 직접 조작할 수 있어서 유용하다.
이런 상황들에서 필터는 DispatcherServlet이 요청을 처리하기 전에 스트림을 조작하는 데 있어 이상적인 선택이 될 수 있다. 요청이나 응답 스트림의 내용을 서블릿이 처리하기 전에 조작해야 할 때, 필터를 활용하면 그러한 작업을 효과적으로 수행할 수 있기 때문이다.
다이어그램으로 과정 시각화
- 이 다이어그램은 클라이언트로부터 GZIP으로 압축된 요청이 서버에 도착하는 순간부터 최종 응답이 클라이언트에게 돌아가기까지의 필터 작동 과정을 보여준다.
- GZIP 압축 요청: 클라이언트가 GZIP으로 압축된 요청을 서버로 보낸다.
- GZIP 해제: 필터가 요청을 가로채서 GZIP 압축을 해제한다.
- JSON 파싱 및 변환: 필터가 요청 본문에 포함된 JSON 데이터를 파싱하고 필요한 형태로 변환한다.
- DispatcherServlet으로 전달: 변환된 요청 데이터가 스프링의 DispatcherServlet으로 전달된다.
- 비즈니스 로직 처리: DispatcherServlet은 요청을 서비스 레이어로 전달해서 비즈니스 로직을 처리하게 한다.
- 응답 스트림 조작: 필터가 응답 데이터 스트림을 필요에 따라 수정하거나 조작한다.
- 최종 응답: 처리된 응답이 클라이언트에게 돌아간다.
이 과정을 통해, 필터가 요청과 응답의 스트림을 직접 조작하는 데 있어 중요한 역할을 한다는 걸 알 수 있다.
6. 마무리
필터의 주요 포인트 요약
- 필터는 서블릿 컨테이너 수준에서 요청과 응답을 가로채는 객체이다.
- 모든 요청(정적, 동적 자원 모두)에 대해 작동하며, DispatcherServlet에 도달하기 전에 실행된다.
- 필터는 요청/응답 스트림 조작, 보안 검사, 로깅 등 다양한 목적으로 사용될 수 있다.
필터를 사용해야 하는 시나리오와 그 이유
- 보안 검사: 인증 토큰 검증과 같은 보안 관련 작업을 위해 필터를 사용한다.
- 요청/응답 로깅: 모든 요청의 세부 정보를 기록하기 위해 필터를 사용한다.
- 스트림 조작: 요청 본문의 압축 해제나 응답의 압축과 같은 스트림 조작을 위해 필터를 사용한다.
📌 결론
이렇게 Spring의 필터(Filter)에 대해서 정리해 봤다.
이전에 사용해보며 간단히는 알고 있던 내용이었지만 지금처럼 이렇게 자세히는 알지 못했다. 다시 공부하면서 부족했던 부분들이 채워지는 기분이다. 다음에는 인터셉터에 대해서도 깊이 학습하도록 해야겠다.
'Spring 기초 > Spring 기초 지식' 카테고리의 다른 글
Spring Boot: 필터에서 doFilter와 FilterChain이란? (2) | 2023.11.05 |
---|---|
웹 개발자를 위한 CORS 이해와 Spring Boot에서의 적용 방법 (1) | 2023.11.05 |
[Spring] 스프링과 자바의 동시성과 병렬 처리 (1) | 2023.10.31 |
Spring: @ControllerAdvice와 AOP를 함께 사용하여 에러 로깅하기 (0) | 2023.10.24 |
Spring Boot: @ControllerAdvice/@RestControllerAdvice로 예외처리하기 (6) | 2023.10.23 |