[Spring] 공통 로직 처리 (Filter, Interceptor, AOP)

yourjin·2023년 3월 13일
0

dev.log

목록 보기
8/13

➕ Topic


  • 회사에서 프로젝트를 하면서, 필요 없어 보이는 Filter를 제거했다가 POST로 요청하는 API에서 body 값을 읽어오지 못하는 경우가 있었다. 내가 작업한 범위가 아니고 다른 분이 수정하면서 생긴 side effect 였다.
  • 덕분에 Interceptor, Filter 등 Spring에서 공통 로직을 처리하기 위해 사용되는 기술들을 공부해볼 수 있었다. 이 포스팅에는 그때 공부한 내용을 정리해 보려고 한다.

➕ Contents


Spring에서 공통 로직 처리: Filter, Interceptor, AOP

개발을 하다보면 공통적으로 처리해야 할 로직이 생기는데, 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. 스프링 빈)에 접근할 수 있다.

Filter vs. Interceptor vs. AOP

Filter

  • Request/Response를 걸러서(filter) 전달한다.
    • ex. 인코딩, XSS 방어 등
  • web.xml에 등록할 수 있다.
    • 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>
  • Spring Boot를 사용하는 경우 FilterRegistrationBean을 생성하거나, @WebFilter + @ServletComponentScan 애노테이션의 조합으로도 적용할 수 있다.

Interceptor

  • Request/Response에 대한 작업을 하기 전에 가로챈다(Intercept).
    • ex. 로그인/권한 체크, 로깅, 프로그램 수행 시간 계산 등
  • Dispatcher Servlet에 등록해서 사용한다.
    • 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>

AOP(Aspect Oriented Programming)

  • OOP(Object Oriented Programming)에서 중복을 줄이기 위해 관점(Aspect)에 따라서 바라보고 처리하는 방법
    • ex. 로깅, 트랜잭션, 에러 처리 등
  • Filter, Interceptor와 달리 메소드 전/후에 자유롭게 추가 가능하다.
  • 주소(url) 뿐만 아니라 파라미터, 애노테이션 등으로 대상 지정이 가능하다.
@Aspect
@Component
public class Logging {
	
	// @Logging 애노테이션이 붙은 메소드 이전에 실행되는 로직
	@Before("@annotation(com.example.common.annotation.Logging)")
  public void annotationTargetLogging(JoinPoint jp) {
			// 공통으로 처리하고 싶은 로직
	}
}

오늘은 이렇게 공통 로직 처리를 위한 다양한 방법을 알아봤는데, 다음에는 이 로직 처리를 하면서 어떤 문제가 발생했는지, 어떻게 해결했는지 정리해보려 한다.

➕ References


profile
make it mine, make it yours

0개의 댓글