
이번에는 Spring 🍃 하면 알아야 할 기본 개념 중 하나인
Filter, Interceptor, AOP 들의 개념과 차이점들에 대해 살펴보겠다.
개발을 하다보면 로그인 관련 처리, 권한 체크, 로그와 같이 공통 기능 이 생긴다.
Filter, Interceptor, AOP 가 이 공통 기능 수행에 대한 다양한 기능을 제공하는데 각각의 특성을 살펴보며, 어떤 공통 기능엔 어떤 것을 사용하면 좋을지 알아보자.

*이미지 출처 : https://sallykim5087.tistory.com/158
Interceptor 와 Filter 는 Servlet 단위에서 실행되고, AOP 는 method 앞에 Proxy 패턴의 형태로 실행된다.
전체적인 흐름은 위의 이미지를 참조하면서, 하나씩 개념을 살펴보겠다.
필터는 스프링 컨텍스트 외부에 존재하여 Java Servlet 에서 제공하는 기능이다.
Servlet 에 대해 알고싶다면 이곳으로!
DispatcherServlet 이전에 실행되어 요청과 응답에 대한 전처리/후처리를 수행한다.
또한 FilterChain 을 통해 여러 필터가 연쇄적으로 동작하게 할 수도 있다.
Filter는 보통 로깅, 인코딩 설정, CORS 처리 등에 사용되는데,
요청이 DispatcherServlet 에 전달되기 전에 헤더 검사하기와 같은 일들을 Filter를 통해 처리하면 좋을 것이다.
Filter 는 servlet 의 Filter 인터페이스를 구현해서 만들 수 있고, 이 인터페이스에는 3가지 메소드가 있다.
@Slf4j
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("Filter 생성!");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("필터 시작!=");
filterChain.doFilter(servletRequest, servletResponse);
log.info("필터 종료!");
}
@Override
public void destroy() {
log.info("Filter 소멸!");
}
}
만든 클래스를 FilterRegistrationBean을 이용해 등록하면 된다.
@Configuration
public class Config{
@Bean
public FilterRegistrationBean firstFilterRegister() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new MyFilter());
return registrationBean;
}
}
인터셉터는 Spring MVC 내에서 요청이 컨트롤러에 도달하기 전이나 뷰가 렌더링되기 전/후에 가로채서 작업을 할 수 있게 해 준다.
보통 HandlerInterceptor 나 AsyncHandlerInterceptor 을 구현해서 사용한다.
핵심 메서드는 3가지가 있다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
return true; // false 반환 시 컨트롤러로 진행되지 않음
}
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {}
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {}
}
구현한 인터페이스는 WebMvcConfigurer를 구현한 클래스에 등록하면 된다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/**") // 적용 경로
.excludePathPatterns("/login"); // 제외 경로
}
}
Spring AOP 는 관심사의 분리 를 실현한 방법인데,
여기서 관심사의 분리란 핵심 로직과 공통 로직 (로깅, 트랜잭션, 보안 등) 을 분리하여 코드 중복 없이 유지보수가 쉬운 구조를 만드는 것이다.
더 정확히 말하자면, 관점 지향 프로그래밍 (AOP) 은 어떤 로직을 헥심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하는 것이다.
Spring AOP 는 프록시 기반 AOP로 메서드 단위의 런타임 AOP 를 제공하고,Spring Bean 에만 적용이 가능하다.
Aspect : 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
Target : Aspect를 적용하는 곳 (클래스, 메서드 .. )
Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
PointCut : JointPoint의 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음
+) 실질적인 구현체인 Advice 의 종류는 아래와 같다.
| Advice 종류 | 어노테이션 | 설명 |
|---|---|---|
| 전처리 | @Before | 메서드 실행 전 |
| 후처리 (정상 시) | @AfterReturning | 메서드가 예외 없이 완료된 후 |
| 후처리 (예외 시) | @AfterThrowing | 예외가 발생한 후 |
| 항상 후처리 | @After | 예외 상관없이 항상 |
| 둘 다 감싸기 | @Around | 메서드 실행 전/후 모두 제어 가능 |
로깅할 때의 아주 간단한 사용 예시를 보자.
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.UserService.getUser(..))")
public void logBeforeUserGet() {
System.out.println("메소드 실행 전...");
}
}
@Aspect : 이 어노테이션을 붙이면 해당 클래스에 스프링 AOP 를 적용할 수 있게 된다. @Component : 이 어노테이션을 통해 Spring Bean 으로 등록한다. 등록을 해야 AOP 가 실질적으로 작동한다.@Before : Advice 의 종류 중 하나로 메서드 실행 전에 작동된다. execution : 포인트컷 표현식 중 하나이다. execution을 사용하면 메소드 실행 시점을 기준으로 동작시킬 수 있다. 이밖에도 within , this , target 등이 있다.| 포인트컷 | 설명 |
|---|---|
| execution | 메소드 실행 시점을 기준으로 동작 (가장 일반적) |
| within | 특정 클래스나 패키지 안의 모든 메소드를 타겟팅 |
| this | 프록시 객체(스프링 AOP에서 생성된 프록시)의 타입 기준 |
| target | 실제 대상 객체의 타입 기준 |
| args | 전달되는 매개변수의 타입 기준 |
| @target | 대상 객체에 어노테이션이 붙었는지 기준 |
| @within | 클래스 수준에서 어노테이션이 붙었는지 기준 |
| @annotation | 특정 어노테이션이 붙은 메소드를 타겟팅 |
| bean | 특정 이름의 빈을 타겟으로 설정할 때 사용 |
포인트컷 표현식은
리턴타입 패키지 클래스 메소드 매개변수 순서로 지정할 수 있다.
만든 표현식들은 && , || , ! 을 사용해서 결합할 수도 있다.
리턴타입의 경우 * 는 모든 리턴타입 허용이고, void, String 등이 있다.
.. 은 여러 개 혹은 생략 가능하다는 의미인데 파라미터에 ..이 있으면, 매개변수와 상관 없이 모든 해당 메소드에 적용한다는 뜻이 되고,
패키지에 ..이 있으면 해당 경로의 모든 하위 패키지를 포함한다는 의미이다.
그래서 위의 코드에서
* com.example.service.UserService.getUser(..) 은 com.example.service 경로의 Userservice 클래스의 getUser 라는 메소드가 실행될 때 적용된다는 말이다.
이때, 모든 리턴 타입을 허용하고, 모든 매개변수 (없는 것도) 허용한다.
정리하자면
Filter 는 스프링 컨텍스트 외부에 존재하여 스프링의 앞단인 DispatcherServlet이 실행되기 전에 실행이 되고,
Interceptor 은 Spring MVC 내에서 요청이 컨트롤러에 도덜하기 전, 뷰가 렌더링 되기 전 후에 작업이 실행되며,
Spring AOP 는 메소드 단위로 실행 시점을 미세하게 조정할 수 있다.
Spring 주요 개념들에 대해 빠르게 간단하게 살펴보았다.
심화적으로 깊게 공부할 수 있는 개념이니, 시간이 될 때 더욱 심층적으로 보아도 좋을 것 같다.
참조
Spring Framework - Filters
Spring Framework - AOP
Spring Framework - Interceptor
https://sallykim5087.tistory.com/158
https://gardeny.tistory.com/35
https://engkimbs.tistory.com/entry/스프링AOP
정저우의 스프링 부트 핵심 가이드
https://hstory0208.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-Aspect-%EC%82%AC%EC%9A%A9%EB%B2%95