Springboot MVC Filter, Interceptor, AOP 차이 실행시점 구현방법
Springboot Filter, Interceptor, AOP 차이 실행시점 구현방법
이번 포스팅에서는 Springboot MVC 모델을 활용해 Filter, Interceptor, AOP를 구현해보고
차이점과 실행시점에 대해서 알아보도록 하겠습니다.
우선 Spring MVC에서 Filter, Interceptor, AOP는 실행 시점의 차이가 있습니다.
Dispatcher-Servlet 이전과 이후로 나눌 수 있는데,
Filter는 Dispatcher-Servlet 이전 (Web Container가 관리),
Interceptor 와 AOP는 Dispatcher-Servlet 이후에 동작합니다. (Spring Container가 관리)
그래서 Filter는 Spring Bean에 접근할 수 없고, Spring MVC를 기준으로 web.xml 에 설정을 하며,
Interceptor와 AOP는 Spring Bean에 접근 할 수 있고, Dispatcher-Servlet에 설정합니다.
Filter 설정
Filter는 javax.servlet.Filter interfece를 Implements 받아 구현하게 됩니다.
Implements 받은 Filter Interface에는 init, doFilter, destroy의 3개의 메서드가 있습니다.
init(default) 은 Filter 초기화 시, doFilter는 Filter 동작 시,
destroy(default)는어플리케이션 종료 시 실행되는 메서드 입니다.
@WebFilter(urlPatterns="/*")
public class TestFilter implements Filter{
private static final Logger LOGGER = LoggerFactory.getLogger(TestFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("Init Filter");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOGGER.info("doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
LOGGER.info("Filter Destroy");
}
}
@WebFilter 는 아래와 같이 초기 파라메터를 설정할 수 도 있습니다.
@WebFilter(urlPatterns="/*", initParams= {@WebInitParam(name="encoding", value="UTF-8")})
위와 같이 TestFilter 클래스를 생성한 후 어플리케이션 메인 클래스에 @ServletComponentScan 을 추가해줍니다.
@ServletComponentScan 은 Springboot 내장 톰캣을 사용하는 경우 @WebFilter, @WebServlet, @WebListener 어노테이션을 스캔하여 Spring Bean으로 등록하는 역할을 합니다.
@ServletComponentScan
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
CmmnVar.CTX = SpringApplication.run(TestApplication.class, args);
}
}
어플리케이션을 실행하면 "Init Filter" 가 구동시점에 출력되고, 페이지 연결 시 "doFilter" 가 출력되는 것을 확인하실 수 있습니다.
Filter는 인코딩, XSS(Cross Site Scripting) 보안처리 등에 사용됩니다.
Interceptor 설정
Interceptor는 org.springframework.web.servlet.HandlerInterceptor를 Implements 받아 구현하게 됩니다.
Implements 받은 HandlerInterceptor에는 preHandle, postHandle, afterCompletion의 3개의 메서드가 있습니다.
doFilter를 강제 구현해야되는 Filter와 달리 HandlerInterceptor의 메서드는 모두 default로 메소드의 구현을 강제하지는 않습니다.
preHandle은 HandlerAdaptor가 Controller를 실행하기 전에 실행되는 메소드이고
PostHandle은 Controller 실행 후 실행되는 메소드,
afterCompletion은 ViewResolver가 리턴해준 view를 렌더링 한 후에 실행되는 메소드 입니다.
public class TestInterceptor implements HandlerInterceptor{
private static final Logger LOGGER = LoggerFactory.getLogger(TestInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
LOGGER.info("Pre Interceptor");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
LOGGER.info("Post Interceptor");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
LOGGER.info("afterCompletion Interceptor");
}
}
TestInterceptor class를 위와 같이 작성한 후에 WebMvcConfigurer를 Inplements 받은 설정 클래스에
addInterceptors 메소드를 Orverride 해줍니다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
/*기타 설정 생략...*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/main.do");
}
}
여기까지 설정 후 어플리케이션을 실행하여 /main.do를 요청하면 아래와 같은 순서로 로그가 출력되게 됩니다.
Init Filter → doFilter → Pre Interceptor → post Interceptor → afterCompletion Interceptor
Interceptor는 Dispatcher-Servlet 이 후에 동작하기 때문에 Spring Bean에 접근 할 수 있습니다.
주로 인증, 권한의 세부설정 호출에 대한 로깅 등에 활용됩니다.
AOP 설정
AOP(Aspect Oriented Programming : 관점지향프로그래밍)는 OOP(Oriented Object Programming : 객체지향프로그래밍) 의 단점을 보완하여 특정 시점에 공통된 기능을 동작하게 해서 유지보수를 용이하게 할 수 있습니다.
Filter, Interceptor와 달리 AOP는 dependency를 추가해 주어야 합니다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
의존성 주입 후 아래와 같이 TestAOP 클래스를 생성합니다. @Aspect, @Component 을 추가하여 Spring Bean에 추가합니다.
@Aspect
@Component
public class TestAOP {
private static final Logger LOGGER = LoggerFactory.getLogger(TestAOP.class);
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void getMapping() {}
@Before("getMapping()")
public void before(JoinPoint jp) {
LOGGER.info("before AOP");
}
@After("getMapping()")
public void after(JoinPoint jp) {
LOGGER.info("after AOP");
}
@Around("getMapping()")
public Object around(ProceedingJoinPoint pjp) {
LOGGER.info("around AOP");
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
@AfterReturning(pointcut="getMapping()", returning="result")
public void afterReturning(JoinPoint jp, Object result) {
LOGGER.info("afterReturning AOP");
}
@AfterThrowing(pointcut="getMapping()")
public void afterThrowing(JoinPoint jp) {
LOGGER.info("afterReturning AOP");
}
}
어노테이션에 대한 설명은 아래의 Link를 참고하세요.
Link : https://aljjabaegi.tistory.com/595
모든 @GetMapping 메소드에 대해서 AOP가 동작하도록 구현된 코드입니다. 어플리케이션 실행 후 /main.do 로 연결하면 아래와 같이 출력됩니다.
Filter Init → doFilter → Pre Interceptor → around AOP → before AOP → afterReturning AOP → after AOP
→ Post Interceptor → afterCompletion Interceptor
Around는 전처리 기능이기 떄문에 다른 Advice와 다르게 ProceedingJoinPoint 의 proceed 메소드를 호출하여 정보를 리턴해주어야 나머지 Advice 들이 동작하게 됩니다.
AOP는 로깅처리나 암/복호화 등 메소드의 실행 전후 처리가 필요한 경우 활용하게 됩니다.
지금까지 Filter와 Interceptor, AOP에 대해서 알아보았습니다.
각각의 동작 시점과 용도의 차이를 이해하고 활용하도록 합시다!