오늘은 Spring AOP의 내부 메커니즘을 깊이 있게 파헤쳐보고, HttpRequestLoggingAdvice 클래스를 예제로 설명하겠습니다. 또한, AOP의 최적화 방식과 @RestControllerAdvice가 왜 Spring의 AOP 방식과 다르게 동작하는지에 대해 알아보겠습니다 😊
AOP(Aspect-Oriented Programming)는 프로그램의 공통 관심사를 핵심 비즈니스 로직과 분리하여 모듈화하는 프로그래밍 패러다임입니다. 스프링 AOP는 프록시 패턴을 기반으로 메서드 실행 전후에 특정 부가 기능을 자동으로 적용할 수 있게 해줍니다.
Spring AOP는 몇 가지 핵심 구성 요소로 이루어져 있습니다. 이를 이해하면 HttpRequestLoggingAdvice가 어떻게 동작하는지 쉽게 파악할 수 있습니다.
@Aspect 어노테이션을 사용하여 정의합니다.@Around는 메서드 실행 전후에 로직을 실행할 수 있도록 합니다.Spring AOP는 두 가지 주요 프록시 메커니즘을 사용합니다: JDK 동적 프록시와 CGLIB 프록시. 각각의 특성과 사용 시점을 이해하는 것은 매우 중요합니다.
final일 경우 프록시 생성이 불가능합니다.Spring AOP는 기본적으로 대상 빈이 하나 이상의 인터페이스를 구현하고 있으면 JDK 동적 프록시를 사용합니다. 그렇지 않거나, proxyTargetClass=true로 설정된 경우 CGLIB 프록시를 사용합니다.
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(false); // 인터페이스 기반 프록시 사용 (JDK 동적 프록시)
return proxyCreator;
}
HttpRequestLoggingAdvice 클래스 분석 📚이제 HttpRequestLoggingAdvice 클래스를 살펴보겠습니다. 이 클래스는 Spring AOP를 활용해 HTTP 요청을 로깅하는 역할을 합니다.
@Component
@Aspect
public class HttpRequestLoggingAdvice {
private static final Logger logger = LoggerFactory.getLogger(HttpRequestLoggingAdvice.class);
@Around("@within(com.jongho.common.annotaition.HttpRequestLogging)")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
logger.info("Method: " + methodName + ", parameters: " + Arrays.toString(args));
return joinPoint.proceed();
}
}
@Component: 이 클래스를 Spring의 빈으로 등록합니다.@Aspect: 이 클래스가 AOP의 Aspect임을 나타냅니다.@Around: 메서드 실행 전후에 특정 로직을 실행할 수 있는 어드바이스를 정의합니다.@within: 특정 어노테이션이 적용된 클래스의 모든 메서드에 어드바이스를 적용합니다.ProceedingJoinPoint: 실제 메서드를 호출하기 위해 proceed() 메서드를 제공합니다.HttpRequestLoggingAdvice의 동작 과정 🔄이제 HttpRequestLoggingAdvice 클래스가 Spring AOP에서 어떻게 동작하는지 단계별로 살펴보겠습니다.
AnnotationConfigApplicationContext가 AppConfig 및 기타 설정 클래스를 로드하여 빈을 정의하고 초기화합니다.@Component 어노테이션이 있는 HttpRequestLoggingAdvice 클래스가 스캔되어 빈으로 등록됩니다.HttpRequestLoggingAdvice 빈을 생성합니다.@Aspect 어노테이션을 통해 이 빈이 어스팩트로 등록됩니다. 스프링은 이 어스팩트를 감지하여 AOP 설정에 추가합니다.DefaultAdvisorAutoProxyCreator의 역할 🤖DefaultAdvisorAutoProxyCreator 빈 등록: AppConfig 클래스에서 DefaultAdvisorAutoProxyCreator 빈이 정의되어 있습니다.
@Configuration
public class AppConfig {
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(false); // 인터페이스 기반 프록시 사용 (JDK 동적 프록시)
return proxyCreator;
}
}
빈 후처리기 역할: DefaultAdvisorAutoProxyCreator는 BeanPostProcessor로 동작하며, 빈이 초기화된 후에 AOP 어드바이저를 검색하고 프록시를 생성합니다.
DefaultAdvisorAutoProxyCreator는 스프링 컨테이너 내의 모든 어드바이저 빈을 검색합니다. 여기서 HttpRequestLoggingAdvice가 정의한 @Around 어드바이저가 포함됩니다.@HttpRequestLogging 어노테이션이 적용된 클래스의 모든 메서드가 포인트컷과 매칭됩니다.MyServiceImpl)이 인터페이스(MyService)를 구현하고 있으며, proxyTargetClass=false로 설정되었기 때문에 JDK 동적 프록시가 생성됩니다.
4. 인터셉터 체인 설정: 매칭된 어드바이저의 어드바이스(LoggingInterceptor)가 인터셉터 체인에 추가됩니다.
프록시 생성 과정에서 Spring AOP는 JDK 동적 프록시를 사용하여 대상 빈을 감싸는 프록시 객체를 생성합니다. 이 프록시는 대상 빈의 인터페이스를 구현하고, InvocationHandler를 통해 메서드 호출을 가로채어 인터셉터 체인을 실행합니다.
@RestControllerAdvice의 독립성 🧩Spring AOP는 프록시 패턴을 기반으로 다양한 최적화 기법을 적용하여 성능을 향상시킵니다. 주요 최적화 방식은 다음과 같습니다:
@RestControllerAdvice의 독립성@RestControllerAdvice는 Spring MVC의 예외 처리 메커니즘을 활용하여 전역적인 예외 처리를 담당합니다. 이는 Spring AOP의 프록시 기반 메커니즘과는 별개로 동작합니다.
@RestControllerAdvice는 예외 발생 시점에 개입하여 예외를 처리합니다.Spring MVC의 예외 처리 체인은 다음과 같이 구성됩니다:
컨트롤러 내부의 @ExceptionHandler:
@ControllerAdvice 또는 @RestControllerAdvice:
기본 예외 처리기:
이 체인은 AOP의 인터셉터 체인과는 별도로 동작하며, 예외 발생 시점에 개입하여 예외를 처리합니다.
이 포스트가 여러분의 Spring AOP와 @RestControllerAdvice에 대한 이해에 도움이 되었길 바랍니다. 😊✨
더 궁금한 점이나 추가적인 설명이 필요하시면 댓글로 남겨주세요! 👍