본문으로 건너뛰기
Advertisement

8.3 CGLIB vs JDK Dynamic Proxy 동작 원리

스프링 AOP는 코드를 컴파일할 때 바이트코드를 직접 뜯어고쳐 조작하는 무거운 방식(AspectJ 네이티브 위버)이 아닌, 런타임 환경에서 프록시(Proxy, 대리자) 객체를 생성하여 타겟을 동적으로 감싸는 프록시 기반 AOP를 사용합니다.

이 프록시를 메모리에 생성하는 방식에는 기술적으로 두 가지 구현체가 대립합니다.

1. JDK Dynamic Proxy

Java 기본 내장 리플렉션(Reflection) API를 사용하여 순수하게 프록시 인터페이스를 생성합니다. 이 방식은 타겟 객체가 반드시 적어도 1개 이상의 인터페이스를 구현(implements)하고 있을 때만 작동합니다.

  • 장점: 자바 내장 표준 기술이므로 구동을 위한 별도의 외부 라이브러리 의존성이 제로입니다.
  • 단점: 구체 클래스(인스턴스 구현체) 타입으로 프록시를 형변환(Casting)할 수 없고, 반드시 100% 인터페이스 타입으로만 의존성 주입(DI)을 받아야 합니다.

2. CGLIB (Code Generation Library)

인터페이스 스펙이 없는 일반 뼈대 구체 클래스라도 런타임에 프록시를 생성할 수 있도록 돕는 범용 외부 바이트코드 조작 라이브러리입니다. (스프링 코어 프레임워크 안에 이미 내장되어 번들링으로 제공됩니다). CGLIB은 **타겟 클래스를 직접 거대하게 상속(extends)**하여 메서드를 오버라이딩하는 프록시 서브클래스를 인메모리에서 파생 생성합니다.

  • 장점: 불편한 인터페이스 설계 강요 없이 바로 프록시 생성이 가능하여 개발 속도가 유연합니다.
  • 단점: 타겟 클래스가 굳게 닫힌 final이거나 타겟 메서드가 private, final로 제어권이 막혀있을 경우, 자바 상속과 오버라이딩이 물리적으로 불가능하므로 결코 프록시 AOP 로직이 침투할 수 없습니다.
Spring Boot의 영리한 기본 전략

Spring Boot 2.0부터는 타겟 객체가 인터페이스를 실제로 구현하고 있든 없든 **무조건 CGLIB 프록시 생성을 강제하는 방식(spring.aop.proxy-target-class=true)이 기본값(Default)**으로 채택되었습니다. JDK Dynamic Proxy 클래스 캐스팅 오류 때문에 발생하는 휴먼 장애 에러를 프레임워크 단에서 원천 차단하기 위한 깊은 배려입니다.

Advertisement