목차
- 서블릿 필터
- 스프링 인터셉터
서블릿 필터와 스프링 인터셉터
웹 애플리케이션에서 요청과 응답을 가로채어 특정 로직을 수행해야 하는 경우가 많다.
이를 위해 서블릿 필터(Servlet Filter) 와 스프링 인터셉터(Spring Interceptor) 를 사용할 수 있다.
두 개념은 비슷하지만 동작 방식과 적용 범위에서 차이가 있다.
비교 항목 | 서블릿 필터 (Servlet Filter) | 스프링 인터셉터 (Spring Interceptor) |
적용 대상 | 모든 요청 (정적 리소스 포함) | 스프링 MVC 컨트롤러 요청 |
실행 시점 | 서블릿 실행 전후 | 컨트롤러 실행 전후 |
주요 메서드 | init(), doFilter(), destroy() | preHandle(), postHandle(), afterCompletion() |
등록 방식 | web.xml 또는 @Component | WebMvcConfigurer를 이용한 등록 |
주된 사용 목적 | 인증, 로깅, CORS 처리 등 | 인증, 로깅, 데이터 가공 등 |
🥷 서블릿 필터 (Servlet Filter)
서블릿 필터는 클라이언트 요청을 가로채어 특정 작업을 수행할 수 있는 기능을 제공한다.
모든 요청과 응답을 처리할 수 있으며, 정적 리소스(예: CSS, JS, 이미지)에도 적용된다.
1️⃣ 서블릿 필터 동작 방식
- 클라이언트가 요청을 보냄
- 필터가 요청을 가로채어 처리
- 서블릿이 요청을 처리
- 필터가 응답을 가로채어 처리
- 클라이언트에게 응답 반환
2️⃣ 주요 메서드 및 설명
@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("필터 초기화");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("요청이 필터를 통과함: " + ((HttpServletRequest) request).getRequestURI());
chain.doFilter(request, response); // 다음 필터 또는 서블릿 호출
System.out.println("응답이 필터를 통과함");
}
@Override
public void destroy() {
System.out.println("필터 제거");
}
}
1) init(FilterConfig filterConfig)
- 필터가 초기화될 때 실행되는 메서드
- 필터의 설정 정보를 로드하거나 초기 자원을 할당하는 역할
2) doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- HTTP 요청이 오면 doFilter 가 호출된다
- 필터의 핵심 메서드로, 요청과 응답을 가로채어 처리
- chain.doFilter(request, response)를 호출하면 다음 필터 또는 서블릿으로 요청이 전달됨
3) destroy()
- 필터가 제거될 때 실행되는 메서드
- 사용한 자원을 해제하는 역할
3️⃣ FilterRegistrationBean을 사용한 필터 등록 방법
1. 필터 클래스 생성:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
public class ExampleFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 초기화
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Before request processing");
chain.doFilter(request, response); // 다음 필터로 요청을 전달
System.out.println("After request processing");
}
@Override
public void destroy() {
// 필터 정리 작업
}
}
- 필터 클래스는 javax.servlet.Filter 인터페이스를 구현해야 한다.
2. FilterRegistrationBean을 사용한 필터 등록:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<ExampleFilter> exampleFilter() {
FilterRegistrationBean<ExampleFilter> registrationBean = new FilterRegistrationBean<>();
// 등록할 필터 인스턴스를 설정
registrationBean.setFilter(new ExampleFilter());
// 필터가 적용될 URL 패턴 설정, /api/*로 설정하면 /api/로 시작하는 URL에만 필터가 적용
registrationBean.addUrlPatterns("/api/*");
// 필터 실행 순서 설정 (숫자가 낮을수록 우선순위가 높음), 여러 개의 필터가 있을 경우 이 값에 따라 실행 순서가 결정
registrationBean.setOrder(1);
return registrationBean;
}
}
- 필터를 FilterRegistrationBean으로 등록하려면 WebConfig 클래스에 @Bean을 추가한다.
- FilterRegistrationBean은 스프링에서 서블릿 필터를 등록하는 데 사용되는 편리한 방법이다.
- 이 방법을 사용하면 web.xml 파일에 등록하지 않고도 필터를 JavaConfig에서 설정할 수 있다.
- 또한, 필터의 순서나 특정 URL 패턴에 대한 필터 적용 등을 세밀하게 설정할 수 있다.
✅ 장점
- @Bean 방식으로 스프링 컨텍스트 내에서 필터를 관리하므로, DI를 통한 필터 의존성 주입이 가능해진다.
- 필터와 관련된 설정을 스프링 환경에 맞게 더 세밀하게 관리할 수 있다.
❌ 단점
- @WebFilter 애너테이션을 사용하는 방식보다 설정이 더 복잡할 수 있다.
- 또한, 필터 체인을 좀 더 세밀하게 다뤄야 할 경우 유용하지만, 간단한 경우에는 오히려 과도한 설정이 될 수 있다.
4️⃣ @WebFilter 애너테이션을 사용한 필터 등록 (서블릿 3.0 이상)
1. 필터 클래스에 @WebFilter 애너테이션 추가:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/api/*") // 필터가 적용될 URL 패턴
public class ExampleFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 초기화
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Before request processing");
chain.doFilter(request, response); // 다음 필터로 요청을 전달
System.out.println("After request processing");
}
@Override
public void destroy() {
// 필터 정리 작업
}
}
- @WebFilter 애너테이션을 사용하여 필터 클래스를 선언할 수 있다.
- 또한, urlPatterns 속성을 사용하여 필터가 적용될 URL 패턴을 지정한다.
2. @ServletComponentScan 애너테이션 사용 (스프링 부트의 경우)
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@ServletComponentScan // @WebFilter가 있는 클래스를 자동으로 등록
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
- 스프링 부트에서는 @ServletComponentScan 애너테이션을 사용하여 서블릿, 필터, 리스너를 자동으로 스캔하여 등록할 수 있다.
- 이 애너테이션은 @SpringBootApplication 애너테이션이 있는 클래스에 추가하면 된다.
✅ 장점
- 코드 기반으로 필터를 선언하고, 별도로 web.xml이나 FilterRegistrationBean을 설정하지 않아도 된다.
- 스프링 부트와 함께 사용할 때 유용하며, 코드가 간결하고 관리가 쉬워진다.
❌ 단점
- 필터에 추가적인 설정(순서, 특정 URL 패턴에 대한 추가 설정 등)이 필요한 경우, FilterRegistrationBean을 사용하는 것이 더 유연하다.
5️⃣ 필터 체인
public class ExampleFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Before request processing");
chain.doFilter(request, response); // 다음 필터로 요청을 전달
// 다음 필터나 서블릿에서 응답을 처리하고 돌아올 때 실행됨.
System.out.println("After request processing");
}
}
- 필터 체인은 여러 개의 필터가 연결되어 순차적으로 실행되는 구조이다.
- FilterChain 객체는 필터들이 처리한 요청을 다음 필터로 전달하며, 마지막 필터는 서블릿으로 요청을 전달한다.
- 각 필터는 doFilter 메서드에서 chain.doFilter(request, response)를 호출하여 다음 필터로 요청을 넘긴다.
🍃 스프링 인터셉터 (Spring Interceptor)
스프링 인터셉터(Spring Interceptor)는 Spring MVC의 요청 처리 과정에서 특정한 지점에 개입하여 요청을 처리할 수 있는 기능을 제공한다. 주로 요청 전후로 특정 작업을 수행하거나, 컨트롤러 메서드 호출 전에 처리하고자 할 때 사용된다. 서블릿 필터와 달리 스프링 인터셉터는 주로 스프링의 DispatcherServlet 처리 과정에 통합되어 동작한다.
1️⃣ 주요 메서드 및 설명
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
Object handler) {
System.out.println("요청 URL: " + request.getRequestURI());
return true; // true여야 요청이 계속 진행됨
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) {
System.out.println("컨트롤러 실행 후");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
System.out.println("응답 완료");
}
}
1) preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- 컨트롤러 실행 전에 호출됨
- 요청을 검사하고 특정 조건에 따라 요청을 차단할 수 있음 (false 반환 시 요청 중단)
2) postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
- 컨트롤러 실행 후, 뷰가 렌더링되기 전에 호출됨
- 응답 데이터 수정, 추가적인 로직 수행 가능
3) afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
- 뷰가 렌더링된 후 호출됨
- 리소스 정리 및 예외 처리 가능
2️⃣ 인터셉터 등록 방법
1. HandlerInterceptor 인터페이스 구현
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ExampleInterceptor implements HandlerInterceptor {
// 요청이 컨트롤러로 넘어가기 전에 호출됩니다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Before handling the request");
return true; // true를 반환하면 다음 인터셉터나 컨트롤러로 넘어감
}
// 컨트롤러 실행 후, 뷰를 렌더링 하기 전에 호출됩니다.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
System.out.println("After handling the request but before rendering the view");
}
// 뷰 렌더링 후에 호출됩니다. 예외 처리도 이곳에서 가능합니다.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("After the request has been completed");
}
}
- 스프링 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현해야 한다.
- 이 인터페이스는 세 가지 주요 메서드를 제공한다.
2. WebMvcConfigurer를 사용한 인터셉터 등록
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ExampleInterceptor()) // 인터셉터 등록
.addPathPatterns("/api/*") // 인터셉터가 적용될 URL 패턴
.excludePathPatterns("/api/ignore/*") // 제외할 URL 패턴
.order(0);// 인터셉터의 순서를 설정, 낮은 숫자는 높은 우선순위를 의미(기본값: 0)
}
}
- 스프링에서는 인터셉터를 WebMvcConfigurer를 통해 등록할 수 있다.
- WebMvcConfigurer 인터페이스를 구현한 클래스에 addInterceptors() 메서드를 오버라이드하여 인터셉터를 설정한다.
3. @Component를 사용한 자동 등록
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class ExampleInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Before handling the request");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
System.out.println("After handling the request but before rendering the view");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("After the request has been completed");
}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final ExampleInterceptor exampleInterceptor;
public WebConfig(ExampleInterceptor exampleInterceptor) {
this.exampleInterceptor = exampleInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(exampleInterceptor)
.addPathPatterns("/api/*");
}
}
- 인터셉터는 @Component 애너테이션을 사용하여 스프링 빈으로 등록할 수 있다.
- 이 경우 WebMvcConfigurer에서 addInterceptor() 메서드를 사용하여 자동으로 등록할 수 있다.
- @Component로 선언된 인터셉터는 스프링의 빈으로 자동 등록되며, WebConfig에서 직접 @Bean을 사용할 필요 없이 스프링이 자동으로 관리한다.
3️⃣ 스프링 인터셉터 호출 흐름
- preHandle : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출)
- preHandle 의 응답값이 true 이면 다음으로 진행하고, false 이면 더는 진행하지 않는다. false 인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝이 나버린다.
- postHandle : 컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출)
- afterCompletion : 뷰가 렌더링 된 이후에 호출된다.
4️⃣ 스프링 인터셉터 예외 상황
예외가 발생시
- preHandle : 컨트롤러 호출 전에 호출된다.
- postHandle : 컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
- afterCompletion : afterCompletion 은 항상 호출된다. 이 경우 예외(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.
afterCompletion은 예외가 발생해도 호출된다.
- 예외가 발생하면 postHandle() 는 호출되지 않으므로 예외와 무관하게 공통 처리를 하려면 afterCompletion() 을 사용해야 한다.
- 예외가 발생하면 afterCompletion() 에 예외 정보(ex)를 포함해서 호출된다.
'MVC' 카테고리의 다른 글
[MVC] Spring MVC와 Dispatcher Servlet (0) | 2025.02.13 |
---|---|
[Spring] Spring MVC vs Spring WebFlux (1) | 2024.12.05 |
[MVC] 파일 업로드 (0) | 2024.08.06 |
[MVC] 스프링 타입 컨버터 (0) | 2024.08.05 |
[MVC] API 예외 처리 @ExceptionHandler (0) | 2024.08.02 |