이전글에서 이어지는 내용

[이커머스 프로젝트] 주문 로직 - 리팩토링 2 (제네릭과 리플렉션)

 

[이커머스 프로젝트] 주문 로직 - 리팩토링 2 (제네릭과 리플렉션)

이전글에서 이어지는 내용[이커머스 프로젝트] 주문 로직 - 전략 패턴 적용(리팩토링) [이커머스 프로젝트] 주문 로직 - 전략 패턴 적용(리팩토링)초기 코드현재 주문로직은 상품을 직접 주문하

mrxx.tistory.com

 

문제 발생

Caused by: java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
    at ecommerce.coupang.service.order.strategy.OrderStrategyProvider.getGenericType(OrderStrategyProvider.java:34) ~[main/:na]
    at ecommerce.coupang.service.order.strategy.OrderStrategyProvider.<init>(OrderStrategyProvider.java:27) ~[main/:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:209) ~[spring-beans-6.2.0.jar:6.2.0]
    ... 49 common frames omitted

리팩토링을 완료한 후 프로젝트를 실행해보니 ClassCastException이 발생하였다
OrderStrategyProvider에서 발생한 오류인데

잘못된 타입으로 캐스팅을 시도해서 발생한 문제란다
리플렉션을 잘 모른채로 사용해서 발생한것같다
한번 디버거로 문제를 찾아보아야겠다

디버깅

여기 부분에 브레이크포인트를 걸어 보았다

문제를 바로 발견할 수 있었다
일단 각각의 Strategy를 가져오는것엔 문제가없지만
Strategy들을 가져올때 CartOrderStrategy$$SpringCGLIB$$... 이렇게 적혀있는 걸 보았다
내가 알기론 CGLIB는 스프링에서 프록시객체를 생성할때 사용하는 라이브러리로 알고있다

그럼 원본 객체가 아닌 프록시 객체인 것이다

이 부분도 확안해보았다 

원래 예상대로면 OrderStrategy가 있어야하는데 프록시들이 보여진다

GPT에게 물어보니 AopUtils.getTargetClass()를 사용하여 원본 클래스를 추출해낼수있다고한다

해결

public OrderStrategyProvider(List<OrderStrategy<?>> orderStrategies) {
    for (OrderStrategy<?> strategy : orderStrategies) {
        Class<?> targetClass = AopUtils.getTargetClass(strategy); // 프록시에서 원본 클래스 추출
        Class<?> genericType = getGenericType(targetClass);
        if (genericType != null)
            strategyMap.put(genericType, strategy);
    }
}

private Class<?> getGenericType(Class<?> strategy) {
    return (Class<?>) ((ParameterizedType) strategy.getGenericInterfaces()[0])
        .getActualTypeArguments()[0];
}

AopUtils.getTargetClass(strategy); 를 추가하여 원본 클래스를 추출하고
getGenericType() 메서드로 넘겨주었다

다시한번 디버거를 통해서 확인해보겠다

 

targetClass에 원본 클래스가 추출되어 잘 동작한다

근데 왜 프록시 객체가 주입되었나 궁금해졌다
또 GPT에게 물어보니 여러가지 조건이있다고한다

1. AOP기능 적용 ( @Transactional, @Async, 커스텀 AOP....)
2. 인터페이스 기반 프록시 생성 (인터페이스를 구현한 빈을 주입받을경우)
3. 순환 참조 방지 
4. 지연 초기화 ( @Lazy )

이번엔 OrderStrategy 인터페이스를 구현한 Strategy들을 주입 받았기 때문에 프록시 객체가 생성된 것 같다
아직 완벽하게 이해하지 못해 추후에 좀더 공부를 해야할 것 같다

그리고 이번에 디버거를 사용해보았는데 아직 디버거 사용하는데 익숙하지않아서 
꾸준히 디버거를 사용해봐야겠다

Junyoung.dev