스프링 핵심 원리 - 고급편 - 섹션13. 스프링 AOP - 실무 주의사항
CS/김영한 스프링 강의

스프링 핵심 원리 - 고급편 - 섹션13. 스프링 AOP - 실무 주의사항

실제로 사용하다가 겪을 수 있는 경우의 수들

 

일단 프록시 클래스를 내부 호출 했을 때 프록시 적용 안되는 문제

 

프록시를 하고 있는 클래스인데 이 클래스 안에서 자신의 내부 함수를 직접 호출하고 있다면 여기엔 프록시가 적용되지 않는다. 외부에서 하면 프록시의 함수를 실행하므로 당연히 된다.

 

참고로 이건 AspectJ 프레임워크를 쓴다면 이렇게 스프링 AOP처럼 프록시 클래스를 만드는게 아닌 저 클래스 함수 안에 코드를 직접 쓰기 때문에 이런일이 없다. 하지만 설정이 너무 복잡하고 이걸 스프링 AOP로 해결할 수 있는 방법도 많아 거의 쓰지 않는다.

 

 

그래서 클래스 내부에서도 자기 함수를 직접 불러오는게 아닌 자신도 프록시 클래스의 함수를 사용하도록 바꿔보자.

생성자로 받으면 아직 빈에 생성되지도 않은 걸 받으려는 순환 오류때문에 setter로 주입받아야 한다. 그나마 이것도 스프링 2.7 이상부터는 금지라 따로 properties 설정해줘야 함.

 

두 번째 대안은 빈에 주입될 때까지 기다렸다가 가져와 사용하는 것..

 

둘 다 된다.

 

 

 

세번째 대안은 구조적으로 바꾸는 것. 사실 이게 제일 자연스럽고 스프링에서도 권장한다고 함.

 

 

 

해당 함수만 밖으로 빼는 것. 그럼 그 클래스용 AOP도 생겨서 프록시 가능하겠지.

 

이게 제일 자연스럽지만 사실 주로 중요한 큰 기능들은 public으로 놓고 작은 기능을 private로 하는데, 왠만하면 큰 기능들인 public들에만 로그 추적 같은 프록시 기능을 부여하지 작은 기능엔 부여하지 않는다. 그래서 굳이 작은 기능에도 AOP를 적용하기 위해 이것만의 클래스로 만들어 밖으로 빼내는 일은 거의 없다. 하지만 만약에 한다면 이런 방법론이 제일 자연스럽다. 그리고 실무에서도 가끔 나오는 실수.

 

 

프록시의 한계를 보자. 캐스팅이 잘 안된다.

 

 

전 이론 보면 프록시를 만드는 방법이 스프링이 만드는 JDK 동적 프록시와 CGLIB 라이브러리로 만드는 방법이 있었는데, 동적 프록시는 인터페이스가 필요했고 CGLIB는 인터페이스가 없어도 그냥 클래스로 만들었다. 이 차이 때문에 위 테스트에서 차이가 나는 것이다.

 

 

 

그래 뭔지 대충 알겠는데 캐스팅 할일 별로 없는 것 같은데 말장난 하는거 아닌가? 왜 알아야 하지?

왜냐면 문제는 의존관계 주입 시에 발생한다.

 

 

 

 

동적 프록시로 하면 Impl 클래스를 못 불러와서 에러뜨고, CGLIB를 사용하면 정상 작동한다. 이 또한 위와 같은 원리

 

 

사실 DI를 지키는게 좋기때문에 인터페이스를 만들테니 동적 프록시를 사용해도 문제없을거다. 그래도 이렇게만 보면 CGLIB가 무조건 좋은거 아닌가? 할 수 있지만 구체 클래스를 상속하기 때문에 생기는 단점들도 안고있다.

 

 

그럼 스프링은 어떤 방법을 권장할까?

 

바로 CGLIB를 사용한다. CGLIB를 사용했을 때의 단점들을 다 해결해놓고 이 라이브러리를 기본으로 채택해 사용한다. 물론 설정으로 동적 프록시를 사용할 수도 있다.