Proxy, Advice, Pointcut와 Self-invocation
Spring Proxy 선택과 호출 경계를 이해해 Transaction·Async·Cache 적용 누락을 진단한다.
이 문서의 목차
Overview
Spring AOP의 효과는 “Annotation이 붙었는가”보다 호출이 Proxy 경계를 통과했는가에 달려 있다. 같은 객체의 this.method()는 Proxy를 다시 거치지 않아 @Transactional, @Async, @Cacheable Advice가 적용되지 않을 수 있다.
핵심 용어
| 용어 | 설명 |
|---|---|
| Target | 실제 Business 객체 |
| Proxy | Target 앞에서 호출을 가로채는 대리 객체 |
| JDK Proxy | Interface 기반 Proxy |
| Class Proxy | Subclass 기반 Proxy(CGLIB 계열) |
| Self-invocation | Target 내부에서 자기 Method를 직접 호출해 Proxy 우회 |
| Interceptor Chain | 여러 Advice의 실행 순서 |
왜 필요한가
“Annotation이 있는데 Transaction이 안 걸린다”는 문제는 DB보다 Object Reference 문제인 경우가 많다. Proxy가 주입된 외부 Bean 호출인지, Target 내부 직접 호출인지 구분해야 한다.
핵심 원리와 내부 동작
Container의 BeanPostProcessor가 대상 Bean을 Proxy로 감싸고 다른 Bean에는 Proxy Reference를 주입한다. 외부 호출은 Proxy→Advice→Target으로 흐른다. Target의 this.inner()는 Target 내부 Dispatch라 Proxy Reference로 돌아가지 않는다.
sequenceDiagram; participant C as Other Bean; participant P as Service Proxy; participant T as Service Target; C->>P: outer(); P->>T: outer(); T->>T: this.inner(); Note over T: inner Advice 우회
Example
@Service
class BillingService {
@Transactional
public void outer() { inner(); }
@Transactional(propagation = REQUIRES_NEW)
public void inner() { /* 새 transaction을 기대하지만 우회 */ }
}해결은 Transaction 경계 자체를 재설계하거나 inner를 별도 책임 Bean으로 분리해 외부 Proxy 호출로 만든다. Self Injection이나 AopContext는 결합과 순환을 늘려 우선 선택이 아니다.
실무에서 발생하는 문제와 Trade-off
Private/Final Method, 객체 직접 생성, Bean 초기화 시점 호출, 잘못된 Pointcut이 적용을 막을 수 있다. Class Proxy는 상속 제약이 있고 JDK Proxy는 Proxy Type을 Interface로 다루는 것이 안전하다. Advice Order는 Retry가 Transaction 안팎 어디서 반복되는지 바꾼다.
흔한 오해
- Public Annotation만 붙이면 어떤 호출에서도 적용되는 것이 아니다.
- Class Proxy가 모든 Final/Private Method를 가로챌 수 없다.
- Transaction이 안 걸릴 때 Annotation Package만 볼 것이 아니라 실제 Bean Type과 호출 경로를 봐야 한다.
- Self Invocation 해결을 위해 무조건 같은 Bean을 자기 자신에게 주입할 필요는 없다.
Production Considerations
Startup 시 Proxy Class와 Advisor를 Debug Log/Test로 확인한다. Transaction Integration Test에서 Rollback·Propagation을 검증한다. Pointcut은 Package와 Annotation을 좁게 쓰고 여러 Aspect Order를 명시한다. Async는 Thread 전환과 Context 전파도 별도로 검토한다.
다른 사람에게 설명한다면
30초: “Spring AOP는 Proxy를 통과한 Method 호출에 Advice를 적용합니다. 같은 객체 내부의 this 호출은 Proxy를 우회해 Transaction·Async·Cache가 안 걸릴 수 있으므로 책임을 별도 Bean으로 분리해 외부 호출로 만드는 것이 일반적입니다.”
2분: Container가 Target 대신 Proxy Reference를 주입하는 과정, JDK/Class Proxy 차이, Self Invocation Sequence, Private/Final과 Advice Order까지 설명한다.
Interview Questions
- Self Invocation에서
REQUIRES_NEW가 안 되는 이유는? - JDK Proxy와 Class Proxy의 차이는?
- Proxy 적용 여부를 어떻게 진단하는가?
- Retry와 Transaction Aspect 순서가 왜 중요한가?
Related Topics
AOP Fundamentals, Bean Lifecycle, @Async를 포함한 비동기 처리 설계와 함께 본다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.