개발을 하다보면 공통적으로 처리해야 할 로직이 생기는데, Java + Spring 기반의 웹 개발에서는 이를 위해 Interceptor, Filter, AOP 등의 기술을 지원한다.
출처: https://goddaehee.tistory.com/154
(너무 잘 정리된 그림이 있어서, 출처를 밝히고 함께 첨부한다.)
이 기술들은 요청의 흐름에서 주요 로직을 수행하기 전/후에 공통 로직을 추가할 수 있도록 도와준다. 각각 처리되는 시점이 다른데 Request가 들어올 때 Filter → Interceptor → AOP를 거친 후에 Controller에 정의된 주요 로직이 수행되고, 반대로 Response로 나갈 때는 AOP → Interceptor → Filter를 거쳐서 나간다.
또 다른 차이점으로는 Filter는 스프링과 관련 없이 Java Servlet에서 제공한다면, Interceptor와 AOP는 스프링에서 제공하는 기술이라는 것이다. 따라서 Interceptor와 AOP는 Filter와 달리 스프링 컨텍스트 안에 존재하는 자원(ex. 스프링 빈)에 접근할 수 있다.
web.xml은 웹 애플리케이션에 대한 설정 파일로 배포할 Servlet 목록, Servlet에 매핑될 주소(url) 등 지정한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
web.xml에서 Dispatcher Servlet을 등록할 수 있다. 설정 정보를 담은 파일(여기서는 dispatcher-servlet.xml)과 매핑 주소(url)을 지정한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
<servlet-name>dispatcher-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/spring/dispatcher/dispatcher-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Dispatcher Servlet 설정 정보에 Interceptor를 등록하면, doDispatch로 Controller에 요청을 위임할 때 HandlerMapping 과정을 거치면서 실행된다. (HandlerMapping 과정에서는 실제 요청을 처리하는 HandlerMethod와 인터셉터들을 갖는 HandlerExecutionChain을 조회한다.)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/exclude/**" />
<bean class="com.example.common.interceptor.RequestInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
@Aspect
@Component
public class Logging {
// @Logging 애노테이션이 붙은 메소드 이전에 실행되는 로직
@Before("@annotation(com.example.common.annotation.Logging)")
public void annotationTargetLogging(JoinPoint jp) {
// 공통으로 처리하고 싶은 로직
}
}
오늘은 이렇게 공통 로직 처리를 위한 다양한 방법을 알아봤는데, 다음에는 이 로직 처리를 하면서 어떤 문제가 발생했는지, 어떻게 해결했는지 정리해보려 한다.