< 2025.01.07 업로드 >
Spring 프레임워크를 공부하다 보면 자주 마주치게 되는 Filter와 Interceptor!
둘 다 웹 요청을 처리하는 공통 관심사를 다루는 방법이지만, 실제로는 꽤 다른 특징과 사용 목적을 가지고 있습니다.
Spring은 개발자들의 효율적인 코드 작성을 위해 다양한 기능들을 제공하고 있는데요, 특히 여러 곳에서 반복되는 공통 작업들을 쉽게 처리할 수 있도록 도와주어 불필요한 코드 중복을 제거할 수 있습니다.
오늘은 이러한 Spring의 강력한 기능 중 Filter와 Interceptor의 차이점과 각각의 활용 방법에 대해 자세히 알아보도록 하겠습니다😎
📌 필터(Filter)란?
DispatcherServlet에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해
부가 작업을 처리할 수 있는 기능을 제공합니다.
DispatcherServlet은 스프링의 가장 앞단에 존재하는 프론트 컨트롤러이므로 Filter는 스프링 범위 밖에서 처리됩니다.
즉, 스프링 컨테이너가 아닌 톰캣과 같은 웹 컨테이너(서블릿 컨테이너)에 의해 관리가 되는 것이고(but, 스프링 빈으로 등록은 된다), DispatcherServlet 전/후에 처리하는 것이다.
주로 요청/응답 로깅, 인증 및 인가 처리, CORS 설정, 데이터 압축 등과 같은 작업에 사용됩니다.
필터(Filter)의 메소드
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
필터를 추가하기 위해서는 javax.servlet의 Filter 인터페이스를 구현해야 합니다.
이 인터페이스는 3가지 메소드를 가지고 있습니다.
1. init()
- 필터 객체를 초기화하고 서비스에 추가하기 위한 메소드
- 웹 컨테이너가 1회 init 메소드를 호출하여 필터 객체를 초기화한다.
- 이후의 요청들은 doFilter를 통해 처리된다.
2. doFilter()
- url-pattern에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 실행된다.
- FilterChain의 doFilter를 통해 다음 대상으로 요청을 전달한다.
- `chain.doFilter()` 전/후에 필요한 처리 과정을 넣어 원하는 처리를 수행할 수 있습니다.
3. destroy()
- 필터 객체를 서비스에서 제거하고 사용하는 자원을 반환하기 위한 메소드
- 웹 컨테이너에 의해 1번 호출된다.
- 호출 이후에는 더 이상 doFilter에 의해 처리되지 않는다.
필터(Filter) 동작 흐름
DispatcherServlet 전에 동작하므로, Spring MVC 내부 로직에 관여하지 않습니다.
1. 클라이언트 요청 ➡️ 필터 체인
클라이언트가 요청을 보내면 웹 서버(서블릿 컨테이너)가 먼저 요청을 필터 체인으로 보냅니다.
2. 필터에서 `doFilter()` 호출
각 필터는 요청을 처리한 후, doFilter() 메서드를 호출하여 다음 필터 또는 서블릿으로 넘깁니다. 필터 체인이 남아있으면 다음 필터로 넘어가고, 남아있지 않으면 서블릿 또는 스프링 컨테이너로 요청이 전달됩니다.
3. 필터 체인 종료 후 ➡️ 스프링 컨테이너
필터 체인이 모두 처리되면 최종적으로 스프링 컨테이너로 요청이 넘어갑니다. 여기서 스프링이 해당 요청에 맞는 컨트롤러를 호출하여 비즈니스 로직을 수행하고, 응답을 반환합니다.
4. 응답 처리 ➡️ 필터 체인 반대 방향
응답도 필터 체인을 따라 거꾸로 전파됩니다. 즉, 마지막 필터에서 시작해 첫 번째 필터까지 응답이 거치며, 각 필터는 응답을 처리하거나 수정할 수 있습니다.
📌 인터셉터(Interceptor)란?
DispatcherServlet이 Controller를 호출하기 전과 후에
요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다.
Filter와 비슷하지만, 더 세밀한 제어를 제공하고 Spring MVC 내부의 처리 흐름에 맞춰 작동합니다.
- DispatcherServlet은 요청을 처리하기 위해 HandlerMapping에 적절한 컨트롤러를 찾도록 요청합니다.
- HandlerMapping은 해당 요청에 대한 실행 체인`HandlerExecutionChain` 객체를 반환합니다.
- DispatcherServlet은 이 HandlerExecutionChain을 이용해 요청을 처리합니다.
HandlerExecutionChain?
요청을 처리할 컨트롤러(Handler)와 이를 실행하기 전에 호출될 Interceptor 목록을 포함하는 객체입니다.
따라서 이 실행 체인은 1개 이상의 interceptor가 등록되어 있다면 순차적으로 interceptor를 거쳐 Controller가 실행되도록 하고, 인터셉터가 없다면 바로 컨트롤러를 실행합니다.
실제 비즈니스 로직과는 분리되어 처리해야 하는 사용자 권한 확인, 요청 데이터 가공, 공통 로직 처리와 같은 기능에 사용되며, Interceptor는 여러 개를 설정할 수 있습니다. (순서 주의)
Interceptor의 메소드
Spring MVC의 `HandlerInterceptor` 인터페이스를 구현하여 클래스를 정의하고 이를 생성해야 인터셉터를 사용할 수 있습니다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
1. preHandle()
컨트롤러 메소드가 실행되기 전 실행된다.
false를 반환하면 요청을 바로 종료한다.
2. postHandle()
대상 핸들러가 호출된 이후(디스패처 서블릿이 이 뷰를 렌더링하기 전) 실행된다.
3. afterCompletion()
요청 처리 및 뷰 렌더링이 완료된 이후 실행된다.
컨트롤러 하위 계층에서 예외가 발생하여도 반드시 호출된다.
afterCompletion은 예외가 발생해도 호출된다.
예외가 발생하면 `postHandle()`은 호출되지 않으므로
예외와 무관하게 공통 처리를 하려면 `afterCompletion()`을 사용해야 한다.
예외가 발생한 경우 예외 정보(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.
즉, `afterCompletion()`에 예외 정보(ex)를 포함해서 호출된다.
📌 Filter와 Interceptor의 차이점
✅ 영역의 차이
두 기능의 가장 큰 차이는 실행 시점에 속하는 영역(Context)에 있습니다.
Filter는 동일한 웹 애플리케이션의 영역 내에서 필요한 자원들을 활용합니다.
웹 애플리케이션 내에서 동작하므로, 스프링의 Context 내부에 접근하기 어렵습니다.
Interceptor의 경우 스프링에서 관리되기 때문에 스프링 내의 모든 객체(빈)에 접근이 가능합니다.
즉, 빈을 관리하는 스프링 Context 내에 있어서 생성된 빈들에 자유롭게 접근할 수 있습니다.
✅ 호출 시점의 차이
영역에서 차이가 나기 때문에 호출 시점도 다릅니다.
Filter는 DispatcherServlet이 실행되기 전, Interceptor는 DispatcherServlet이 실행된 후에 호출되며
Interceptor는 DispatcherServlet이 실행되며 호출됩니다.
✅ Request, Response 객체 조작 가능 여부
Filter는 request/response를 조작할 수 있지만, Interceptor는 조작할 수 없습니다.
여기서 요청/응답을 조작한다는 것은 내부 상태를 변경한다는 것이 아니라 다른 객체로 변경된다는 의미입니다.
`필터(Filter)`가 다음 필터를 호출하기 위해서는 필터 체이닝(다음 필터 호출 `doFilter()`)을 해주어야 합니다. 그리고 이때 Request/Response 객체를 넘겨주므로 우리가 원하는 Request/Response 객체를 넣어줄 수 있습니다.
public MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 개발자가 다른 request와 response를 넣어줄 수 있음
chain.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse());
}
}
하지만 `인터셉터(Interceptor)`의 경우 HandlerExecutionChain을 통해 여러 인터셉터 목록을 가지고 있고 for문으로 순차적으로 실행시킵니다.
- true 반환 → 다음 인터셉터가 실행되거나 컨트롤러로 요청이 전달됨.
- false 반환 → 요청이 중단됨.
따라서 인터셉터는 다른 request, response 객체를 넘겨줄 수 없습니다.
🚀 Filter vs. Interceptor 차이 정리 요약
특징 | Filter | Interceptor |
관리 주체 | 서블릿 컨테이너(Tomcat, Jetty 등) | Spring 컨테이너 |
위치 | DispatcherServlet 이전 | DispatcherServlet 이후 |
적용 범위 | 애플리케이션 전역 (Servlet 기준) | Spring MVC와 관련된 요청 |
주요 사용 목적 | 요청/응답 로깅, 인증, CORS 처리 | 권한 확인, 비즈니스 로직 전/후 처리 |
구현 클래스 | javax.servlet.Filter | org.springframework.web.servlet.HandlerInterceptor |
Spring Bean 등록 여부 | 가능 | 반드시 Spring Bean으로 등록 |
🌟 실전에서의 활용하기
Filter 사용 사례
- CORS 설정: 다른 도메인의 요청 허용
- JWT 토큰 검증: DispatcherServlet에 도달하기 전에 유효성 검사
- 요청/응답 로깅: HTTP 요청/응답 추적
Interceptor 사용 사례
- 권한 확인: 요청이 특정 권한을 요구하는지 확인
- 공통 데이터 삽입: Controller 메서드 실행 전에 공통 데이터를 모델에 추가
- 성능 로깅: Controller의 요청 처리 시간 측정
🗂️ References
[Spring] 필터(Filter)와 인터셉터(Interceptor)의 개념 및 차이
[Spring] 필터(Filter) vs 인터셉터(Interceptor) 차이 및 용도 - (1)
👀 SSAFY의 다양한 소식과 이야기를 더 알고 싶다면
📌 SSAFYcial 인스타그램
📌 SSAFY 홈페이지
'SSAFY' 카테고리의 다른 글
[싸피셜이 알려드림: 기술편] DispatcherServlet이 뭘까? (0) | 2024.12.22 |
---|---|
[싸피셜이 알려드림: SSAFY편] 게임으로 평가받는다? 배틀싸피 등장! (2) | 2024.12.22 |
[싸피셜이 알려드림: SSAFY편] SSAFY 12기 1학기 최종 관통 프로젝트 회고 (1) | 2024.12.21 |
[싸피셜이 알려드림: SSAFY편] SSAFY 1학기 후기 (1) | 2024.12.21 |
[싸피셜이 알려드림: 기술편] Servlet이 뭘까? (0) | 2024.12.21 |