Backend/Spring

AOP, Filter, Interceptor의 정의와 차이점 (3) - AOP편

Juwon2106 2022. 1. 11. 16:32
728x90

process flow

AOP ( 관점 지향 프로그래밍 : Aspect Oriented Programming) 란?

AOP는 Ioc / DI, 서비스 추상화와 더불어 스프링의 3대 기반 기술 중 하나입니다.

 

AOP는 스프링 기술 중에 가장 이해하기 힘든 난해한 용어와 개념을 가진 기술로 유명합니다.

 

AOP를 이해하려면 OOP를 보조하거나 대체하려는 AOP라는 개념 뒤에 감춰진 

 

필연적인 등장 배경과 스프링이 AOP 기술을 도입한 이유,  AOP 도입을 통해 얻는 장점이 무엇인지에 대한 충분한

이해가 필요합니다.

 

AOP Concepts

AOP는 OOP ( 객체 지향 프로그래밍 : Object Oriented Programming )을 보완하는 확정적인 개념

( OOP를 대신하는 개념이 아닌, OOP를 더욱 빛나게 혹은 더욱 OOP 답게 사용할 수 있도록 도와주는 개념 )

 

객체 지향의 프로그래밍을 했을 때 중복을 줄일 수 없는 부분을 줄이기 위해 종단면( 관점 )에서 바라보고 처리합니다.

 

Aspect

  1. 구현하고자 하는 로깅, 트랜잭션, 권한 등의 기능을 의미한다.
  2. 한 개 이상의 포인트 컷( Pointcut )과 어드바이스( Advice )의 조합으로 만들어진다.

 

Join Points

  1. 관점( Aspect )을 삽입하여 Advice가 적용될 수 있는 위치를 의미한다.
  2. Method를 호출하는 시점, 예외가 발생하는 시점 등과 같이 특정 작업이 실행되는 시점을 의미하기도 한다.

 

Pointcuts

  1. Advice를 적용할 Join Point를 선별하는 과정이나 그 기능을 정의한 모듈을 의미한다.
  2. 패턴 매칭을 이용하여 어떤 Join Point를 사용할 것인지 결정한다.

 

Advice

  1. Aspect( 관점 )의 구현체로 Join Points에서 실행되어야 하는 코드 ( 실제로 AOP 기능을 구현한 객체 )
  2. Advice는 Join Point와 결합하여 동작하는 시점에 따라 5개로 구분된다.

 

Advice의 종류

@Before : 대상 메서드( Join Points )의 실행 전 Advice

@After : 대상 메서드 ( Join Points )에서 성공적으로 리턴된 후 실행되는 Advice

@After-returning : 대상 메서드 ( Join Points )에서 성공적으로 리턴된 후 실행되는 Advice

@After-throwing : 예외 발생 후 실행되는 Advice 

@Around : 대상 메서드의 수행 전 / 후 ( Join Points의 전 / 후 과정에 수행되는 Advice )

 

Target

  1. Advice를 받을 대상, 즉 객체를 의미한다.
  2. 비즈니스 로직을 수행하는 클래스이거나 프락시 객체가 될 수 있다.

 

Weaving

  1. Pointcut에 의해서 결정된 Joinpoint에 지정된 Advice를 삽입하는 과정

 

AOP 사용 방법 ( 로깅 저장 예제 )

Maven

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <version>2.6.2</version>
</dependency>
    
<dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.0.1-jre</version>
</dependency>

// class

package com.example.lolapp.config;

// Guava: Google Core Libraries For Java
import com.google.common.base.Joiner;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@Aspect
class RequestLoggingAspect {

	private static final Logger logger = LoggerFactory.getLogger(RequestLoggingAspect.class);

	private String paramMapToString(Map<String, String[]> paramMap){
		return paramMap.entrySet().stream()
				.map(entry -> String.format("%s -> (%s)", entry.getKey(),
						Joiner.on(",").join(entry.getValue())))
							.collect(Collectors.joining(", "));
	}
    
	// pointcut을 등록하는 부분
	@Pointcut("within(com.example.lolapp.controller..*)")
	private void onRequest() {}
	
    // Advice를 등록하는 부분
	@Around("com.example.lolapp.config.RequestLoggingAspect.onRequest()")
	public Object doLogging(ProceedingJoinPoint pjp) throws Throwable{
		HttpServletRequest request =
				( (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
		Map<String, String[]> paramMap = request.getParameterMap();
		String params = "";
		long start = System.currentTimeMillis();

		if(!paramMap.isEmpty()){
			params = " [" + paramMapToString(paramMap) + "]";
		}

		try{
        	// Around advice는 메소드의 실행 전, 후, 접근, 반환 값까지 바꿀수 있는 가장 강력한 Advice입니다
            // 이 부분에서는 Controller의 메소드가 실행되는 부분입니다.
			return pjp.proceed( pjp.getArgs() );
		}finally {
			long end = System.currentTimeMillis();
			logger.info( "Request : {} {}{} < {} ({}ms)", request.getMethod(),
					request.getRequestURI(), params, request.getRemoteHost(), end - start );
		}
	}
}

logger ( slf4j )를 통해 Request에 사용되는 Method 이름과 URL, 매개변수, 메서드 경과시간을 확인할 수 있습니다.

 

위의 예제 코드는 Aspect를 사용하는 것뿐이지 로깅 자체의 기능은 Interceptor에서 구현하는 것이 올바른 방식입니다.

AOP, Filter, Interceptor의 차이점

Logging, transaction, Exception등 Spring의 Method에서 좀 더 세밀히 조정 혹은 작업이 있을 때 사용합니다.

 

Interceptor는 Filter와 달리 메소드 전 후의 지점에 자유롭게 설정이 가능합니다.

 

Interceptor와 Filter는 요청 URL의 대상을 구분해서 개입합니다.

 

AOP는 주소, 매개변수, 애노테이션 등의 다양한 방법으로 대상을 지정할 수 있습니다.

 

AOP의 Advice와 HandlerInterceptor의 가장 큰 차이점으로는 매개변수의 차이입니다.

 

Advice의 경우 JoinPoint, ProceedingJoinPoint 등을 활용해서 호출합니다.

 

HandlerInterceptor는 Filter와 유사하게 HttpServletRequest, HttpServletResponse를 매개변수로 사용합니다.

 

이렇게 AOP, Filter, Interceptor의 정의와 각각의 기능으로부터 오는 차이점을 알아보았습니다.

 

구분 Filter Interceptor AOP
실행 위치 Servlet Servlet Method
실행 순서 1 2 3
설정 위치 web.xml xml or java xml or java
실행 메서드 init(), doFilter(), destory() preHandler(), postHandler(), afterCompletion() Pointcut으로 @After,
@Before, @Around
위치를 지정

 

728x90