안녕세계
[Spring] AOP(Aspect Oriented Programming) 본문
[Spring] AOP(Aspect Oriented Programming)
Junhong Kim 2025. 2. 2. 19:16ProxyFactory
Spring의 ProxyFactory는 AOP를 구현할 때 핵심 역할을 하는 도구입니다. ProxyFactory를 사용하면 target 객체를 감싸는 proxy 객체를 동적으로 생성할 수 있으며, 이 proxy 객체를 통해 메서드 호출 시 부가적인 행동(예: 로깅, 트랜잭션 관리 등)을 추가할 수 있습니다. Spring에서 ProxyFactory를 활용해서 동적 proxy를 만드는 방법은 JDK 동적 프록시와 CLIB이 존재합니다.
- JDK 동적 proxy: 인터페이스를 기반으로 동적 proxy 생성 (리플렉션 사용)
- JDK 동적 proxy는 인터페이스 기반으로 만들었기 때문에, 구현 클래스로 타입 캐스팅이 불가능합니다.
- CGLIB: 인터페이스 없이 구체 클래스만으로도 동적 proxy 생성 가능 (바이트 코드 조작)
- CGLIB로 만든 proxy는 구체 클래스 기반이므로, 인터페이스로 타입 캐스팅이 가능합니다.
ProxyFactory는 대상에 인터페이스가 있으면 JDK 동적 proxy로 proxy 객체를 생성하고, 대상에 인터페이스가 없으면 CGLIB로 proxy 객체를 생성합니다. 참고로 SpringBoot에서는 기본적으로 proxyTargetClass 값이 true 이며, 이때 인터페이스 여부와 상관 없이 CGLIB 기반으로 proxy 객체를 생성합니다.
- true: CGLIB
- false: JDK 동적 proxy (인터페이스가 없으면 CGLIB 사용)
AOP
AOP에서 사용되는 주요 용어는 크게 Pointcut, Advice, Advisor 가 있으며 상세 내용은 다음과 같습니다.
- Pointcut: 어디에 부가 기능을 적용할지
- Advice: 부가기능
- Advisor: Pointcut + Advice를 결합한 구성요소
Spring은 AOP를 적용할 때, 최적화를 진행해서 proxy 객체는 하나만 만들고, 하나의 proxy 객체에 여러 Advisor를 적용합니다. 즉, 하나의 target 객체에 여러 AOP가 적용되어도, Spring AOP는 target 마다 하나의 proxt 객체만 생성합니다.
BeanPostProcessor
BeanPortProcessor(빈 후 처리기)를 사용해서 객체를 조작하거나, 다른 객체로 바꿔지기 가능합니다. 즉, 원본 객체를 proxy 객체로 변환해서 반환할 수 도 있습니다.
AnnotationAwareAspectJAutoPrxoyCreator는 Spring AOP에서 proxy 객체를 자동 생성을 담당합니다. 이 클래스는 Spring Container 초기화 과정 중에 BeanPostProcessor로 동작하며, 애플리케이션 내의 모든 빈을 검사하여 AOP 관련 Advisor를 적용할 필요가 있는 빈을 자동으로 감지하고 proxyt로 감싸는 역할을 수행합니다. 즉, @Aspect를 자동으로 인식해서 proxy를 만들고 AOP를 적용해줍니다.
참고로 Aspect는 Advice와 Pointcut을 포함하는 더 높은 수준의 개념입니다. 하나의 Aspect는 여러 Advisor를 포함할 수 있으며, 이를 통해 다양한 부가기능 적용이 가능합니다. (Advisor를 한 클래스 내에서 선언적으로 관리할 수 있음)
- Advisor: 프로그래밍 방식 (=직접 구현)
- Asepct: 어노테이션 방식 (=선언적)
Pointcut
Pointcut은 생성 단계와 사용 단계에서 사용됩니다.
- 생성 단계: 특정 Bean 프록시 적용 여부를 판단하며, 조건에 맞는게 없으면 proxy 객체를 생성하지 않습니다.
- 사용 단계: 특정 Bean이 proxy 객체로 생성되었어도, Advice 대상인 경우에만 proxy -> target을 호출하고. Advice 대상이 아닌 경우 target만 호출합니다.
Spring에서 Pointcut은 AspectJExpressionPointcut을 사용하는데, 이는 AspectJ라는 AOP에 특화된 Pointcut 표현식이 적용 됩니다.
- 예: execution(* hello.proxy.app..*(..))
Advisor
하나의 proxy 객체에는 여러 Advisor가 적용될 수 있으며, 프록시 자동 생성기에 의해 생성되는 상황별로는 다음과 같습니다.
- Advisor(1)의 Pointcut만 만족 -> proxy 1개 생성, proxy에 Advisor(1)만 포함
- Advisor(1), Advisor(2)의 Pointcut 만족 -> proxy 1개 생성, proxy에 Advisor(1), Advisor(2) 모두 포함
- Advisor(1), Advisor(2)의 Pointcut 불만족 -> proxy 생성하지 않음
Aspect
Aspect는 부가기능과 부가기능을 어디에 적용할지 정의한 것 입니다. Spring이 제공하는 Advisor도 Advice(부가기능)과 Pointcut(적용대상)을 갖고 있어 개념상 하나의 Aspect 입니다. Spring은 @Aspect로 Pointcut과 Advice로 구성된, Advisor 생성 기능을 지원합니다.
@Around("execution(...)") // Advice 종류 + pointcut
public Object execute(ProceedingJoinPoint joinPoint) {
// ... // Advice 로직(1)
joinPoint.proceed(); // 실제 target 호출
// ... // Advice 로직(2)
}
여기서 jointPoint는 실제 호출 target, 전달 인자, 어떤 객체/메서드가 호출되었는지에 대한 정보를 포함합니다.
AOP 적용 방식
AOP를 적용하는 방식으로는 3가지가 있습니다.
컴파일 시점
- AspectJ 컴파일러를 통해 원본 로직에 부가 기능 로직을 추가
- 원본 로직에 부가 기능 로직을 추가하는 것을 weaving이라고 합니다. (Aspect와 실제 코드를 연결해서 붙이는 것)
클래스 로딩 시점
- .class 파일을 조작한 다음 Java에 올립니다. -> 클래스로더 조작기 사용 (Instrumentation)
- 이 시점에 Aspect 적용하는 것을 Load Time Weaving 이라고 합니다.
런타임 시점
- 실제 target을 유지하고, proxy를 통해 부가 기능을 적용합니다.
- 즉, 항상 proxy 객체를 통해야 부가 기능을 제공할 수 있습니다. (Spring AOP는 이 방법을 사용)
Proxy 방식을 사용하는 Spring AOP는 proxy를 사용하기 위해 메서드 실행 시점에만 AOP를 적용할 수 있습니다. 즉, Spring AOP의 joinPoint는 메서드 실행 시점으로 제한됩니다. 또한, Spring AOP는 Spring Container가 관리할 수 있는 Spring Bean에만 AOP를 적용할 수 있는데, 이는 BeanPostProcessor를 거쳐야하기 때문입니다.
AOP 용어
JoinPoint
- Advice가 적용될 수 있는 위치
- JoinPoint는 추상적인 개념이며, AOP를 적용할 수 있는 모든 지점을 의미
- 단, Spring AOP는 Proxy 방식을 사용하므로, Spring AOP의 JoinPoint는 메서드 실행지점만 가능
Pointcut
- JoinPoint (메서드 실행, 생성자 호출, 필드 접근 등) 중에서 Advice가 적용될 위치를 선별하는 기능
- 주로 AspectJ 표현식 사용
- Spring AOP는 Proxy를 사용하므로 메서드 실행 지점만 Pointcut으로 선별 가능
Target
- Advice를 받는 객체 (Pointcut으로 결정됨)
Advice
- 부가 기능
- JoinPoint에서 Aspect에 의해 취해지는 조치
- Around, Before, After 등 다양한 Advice 종류가 있음
Aspect
- Advice + Pointcut을 모듈화한 것 (@Aspect)
-여러 Advice와 Pointcut이 함께 존재
Advisor
- 하나의 Advice와 하나의 Pointcut으로 구성 (Spring AOP에서만 사용되는 특별한 용어)
Weaving
- Pointcut으로 결정한 target의 JoinPoint에 Advice를 적용한 것
- Weaving을 통해 핵심 기능 코드에 영향을 주지 않고, 부가 기능을 추가할 수 있음
참고
'Server > Spring' 카테고리의 다른 글
[Spring] Transaction Propagation (0) | 2025.02.15 |
---|---|
[Spring] Bean Life Cycle (0) | 2025.01.05 |
[Spring] @PostPersist와 TransactionPhase.AFTER_COMMIT (0) | 2024.06.30 |
[Spring] SSE란? (feat. SseEmitter) (0) | 2024.03.03 |
[Spring] 그라파나와 로키로 애플리케이션 로그 조회하기 (0) | 2023.04.22 |