들어가며

결론부터 이야기하자.

스프링이 사용하는 다이나믹 프록시에는 2가지 방법이 있다.

스프링 부트에서는 CGLib Proxy 방식으로 AOP를 사용한다.

👉JDK Dynamic Proxy, CGLib Proxy를 이해해보고 왜 스프링 부트는 CGLib 방식을 채택하게 되었는지 알아보자.


프록시

AOP를 공부한다면 빠지지 않는 단어가 프록시이다. 프록시 패턴의 개념은 이전 파트에서 공부했지만 간단한 예제로 리마인드를 해보자.

프록시 패턴을 체크카드와 비유해보자. 체크 카드로 결제를 하면 체크카드와 연관된 계좌에서 돈이 빠져나간다. 체크카드는 단순히 Proxy 역할을 해주고 계좌는 외부로 숨길수가 있다.

'신입 개발자 유씨에게 업무가 내려왔다. 해당 업무는 각 메소드별 실행 시간을 측정해서 가장 시간이 걸리는 메소드를 찾아내야했다. 유씨는 평소 디자인패턴을 공부하였기 때문에 프록시 패턴을 사용하여 관심사를 분리하여 해결해보기로 했다. 완성된 코드는 다음과 같다.'

public interface PrgrmsSerivce {

    void doSomething();

    void doOtherThing();
}
@Service
public class ServiceA implements PrgrmsSerivce {

		@Override
    public void doSomeThing() {
        System.out.println("hi");
    }

    @Override
    public void doOtherThing() {
        System.out.println("hello");
    }
}

아래의 코드가 중요한데 ServiceA객체를 주입 받아 ServiceA의 메소드 앞뒤로 시간을 측정하는 코드로 감싼다.

@Service
public class ProxyService implements PrgrmsSerivce {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final PrgrmsSerivce service;

    public ProxyService(@Qualifier("serviceA") PrgrmsSerivce service) {
        this.service = service;
    }

    @Override
    public void doSomething() {
        **StopWatch stopWatch = startStopWatch();**
        service.doSomething();   // 실제 서비스레이어의 메소드를 호출
        **stopAndPrint(stopWatch);**
    }

    @Override
    public void doOtherThing() {
        **StopWatch stopWatch = startStopWatch();**
        service.doOtherThing();  // 실제 서비스레이어의 메소드를 호출
        **stopAndPrint(stopWatch);**
    }

    private StopWatch startStopWatch() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        return stopWatch;
    }

    private void stopAndPrint(StopWatch stopWatch) {
        stopWatch.stop();
        logger.info(stopWatch.prettyPrint());
    }

}

<aside> 💡 신입 개발자 유씨는 평소 알고 있던 프록시 패턴으로 요구사항을 해결하였다. 하지만 곰곰히 생각하다보니 문제점을 발견하였다.

</aside>

기존 프록시 패턴의 문제점