Spring AOP를 공부하다 보면 ProxyFactory라는 개념을 접하게 됩니다. 처음에는 다소 생소할 수 있지만, 프록시의 생성 과정을 직접 다뤄볼 수 있기 때문에 AOP의 내부 동작 원리를 이해하는 데 도움이 됩니다.
실제로 요즘 Spring Boot 기반의 실무 개발에서는 @Aspect 기반의 선언적 AOP를 선호하고 사용하지만, 이면엔 ProxyFactory가 프록시 생성을 담당하는 도구로 활용되고 있습니다.
따라서 이번 글에서는 ProxyFactory의 역할과 동작을 간단하게 정리해보려고 합니다.
프록시 팩토리(Proxy Factory)란?
ProxyFactory는 Spring AOP에서 프록시 객체를 생성하기 위한 핵심 클래스입니다.
AOP(관점 지향 프로그래밍)에서는 핵심 기능 외에 부가 기능(예: 로깅, 트랜잭션 처리 등)을 메서드 실행 전후에 삽입하고 싶은 경우가 많은데, 이때 대상 객체(Target)에 프록시를 씌워 메서드 실행 흐름을 제어합니다.
이 프록시 객체를 생성해주는 도구가 바로 ProxyFactory입니다.
프록시 팩토리의 구조
프록시팩토리 전체 동작 흐름

- “ProxyFactory는 대상 객체를 감싸고, 조건에 따라 프록시 전략을 선택하여 클라이언트에 프록시를 반환합니다.”
프록시 팩토리 구조
ProxyFactory는 프록시 객체를 생성하기 위해 다양한 설정을 제공합니다. 주요 구성 요소들을 하나씩 살펴보겠습니다.
1. 대상 객체(Target)
- 프록시가 감싸서 기능을 확장할 실제 비즈니스 객체입니다.
- ProxyFactory는 이 객체를 기준으로 메서드를 가로채어 어드바이스(Advice)를 적용합니다.
MyService target = new MyService();
ProxyFactory proxyFactory = new ProxyFactory(target);
2. Advice
- 메서드 실행 전/후 혹은 예외 시 수행할 부가 로직입니다.
- MethodInterceptor, BeforeAdvice, AfterReturningAdvice 등 다양한 종류의 어드바이스를 적용할 수 있습니다.
proxyFactory.addAdvice(new TimeAdvice());
3. Proxy 전략 (JDK 동적 프록시 vs CGLIB)
- ProxyFactory는 대상 객체에 따라 JDK 동적 프록시 또는 CGLIB 프록시 중 적절한 방법을 선택합니다.
- 인터페이스가 존재하면 JDK 동적 프록시
- 구체 클래스만 있으면 CGLIB 프록시
- 명시적으로 사용할 프록시 방식을 지정할 수도 있다
// CGLIB 프록시를 사용하도록 설정
proxyFactory.setProxyTargetClass(true);
프록시 팩토리 내부 흐름

- Client → Proxy 객체 요청
- 프록시 내부 로직 실행
- 대상 클래스가 인터페이스를 구현하고 있는지 여부에 따라 JDK 동적, CGLIB 프록시를 동적으로 선택 - Advice 적용
- Target 메서드 실행
프록시 팩토리 예제 코드
Spring의 ProxyFactory는 내부적으로 프록시 대상 객체(target)의 구조를 분석한 뒤, 어떤 방식으로 프록시를 생성할지 결정합니다.
이때 핵심 기준은 타겟 클래스에 인터페이스가 존재하는지 여부입니다.
1. JDK 동적 프록시
// 1. 프록시 대상 객체 생성 ( 인터페이스 기반 )
ServiceInterface target = new ServiceImpl();
// 2. ProxyFactory를 생성하고 프록시의 대상 객체로 target을 설정
ProxyFactory proxyFactory = new ProxyFactory(target);
// 3. 프록시 객체가 호출될 때 적용할 공통 기능(Advice) 을 설정
proxyFactory.addAdvice(new TimeAdvice());
// 4. 프록시 객체를 생성
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
System.out.println("Target Class: " + target.getClass());
System.out.println("Proxy Class: " + proxy.getClass());
// Target Class: class study.proxyfactory.common.service.ServiceImpl
// Proxy Class: class jdk.proxy3.$Proxy13
- 프록시 대상 객체 생성
- 프록시를 적용할 타겟 객체(target) 를 생성합니다.
- 여기서 ServiceImpl은 ServiceInterface를 구현한 클래스입니다. - ProxyFactory를 생성하고 프록시의 대상 객체로 target을 설정
- 이 시점에 ProxyFactory는 내부적으로 타겟 객체가 인터페이스를 구현 했는지 판단하고 이에따라 JDK 동적 프록시 CGLIB 중 하나를 선택할 준비를 합니다. - 프록시 객체가 호출될 때 적용할 공통 기능(Advice) 을 설정
- 프록시 객체를 실제로 생성
- getProxy() 호출 시점에 JDK 동적 프록시 객체가 생성되며 이 프록시는 ServiceInterface를 구현하고, 메서드 호출시 TimeAdvice가 먼저 실행되도록 연결
2. CGLIB 프록시
// 1. 프록시 대상 객체 생성 ( 클래스 기반 )
ConcreteService target = new ConcreteService();
// 2. ProxyFactory를 생성하고 프록시의 대상 객체로 target을 설정
ProxyFactory proxyFactory = new ProxyFactory(target);
// 3. 프록시 객체가 호출될 때 적용할 공통 기능(Advice) 을 설정
proxyFactory.addAdvice(new TimeAdvice());
// 4. 프록시 객체를 생성
ConcreteService proxy = (ConcreteService) proxyFactory.getProxy();
System.out.println("Target Class: " + target.getClass());
System.out.println("Proxy Class: " + proxy.getClass());
// Target Class: class study.proxyfactory.common.service.ConcreteService
// Proxy Class: class study.proxyfactory.common.service.ConcreteService$$SpringCGLIB$$0
JDK 동적 프록시 예제와 유사하며 차이점은 프록시 대상 객체가 인터페이스 기반이 아닌 클래스 기반입니다.
- Proxy Class의 출력 결과가 CGLIB Proxy 객체로 생성된 것을 확인할 수 있습니다.
📌 정리
JDK 동적 프록시와 CGLIB처럼 대상 객체의 구조에 따라 달라지는 처리 방식이 처음엔 꽤 손이 많이 가는 작업이라고 느껴졌습니다. 하지만 역시 이런 고민들은 저만의 고민이 아니었고, 이미 많은 개발자들이 겪고 해결한 문제였습니다.
Spring에서는 이러한 복잡함을 다양한 도구로 추상화해, 개발자가 일일 생성 방식을 고민하지 않아도 되도록 잘 설계해 두었습니다.
이번 정리 통해 AOP의 기반이 되는 구조와 흐름을 좀 더 깊이 있게 이해할 수 있었고, 앞으로 더 복잡한 구조를 설계할 때 좋은 밑거름이 될 것 같습니다.
'개발' 카테고리의 다른 글
| Spring 빈 후처리기(BeanPostProcessor)? (2) | 2025.05.29 |
|---|---|
| JDK 동적 프록시, CGLIB (0) | 2025.05.26 |
| 리플렉션을 쓰기 전에 생각해야 하는 것들 (0) | 2025.05.24 |