카테고리 없음

Spring boot는 왜 cglib를 선택했을까?

머룽 2023. 4. 22. 14:39
제목 그대로 Spring boot는 왜 cglib를 선택했을지 고민해보자. 내가 고민할건 아니고.. 살짝 제목의 오해가 있을 수 있으니 좀 더 자세히 말해보자. Spring은 기본적으로 aop 메커니즘은 두가지가 있다. 하나는 jdk 동적 프록시와 다른 하나는 cglib 이용한 프록시이다. 자세한 것은 예전에 포스팅한 부분이 있으니 참고하면 되겠다. 참고 : http://wonwoo.ml/index.php/post/1576 두개의 차이점을 알았으니 (참고에서..) 좀 더 자세히 살펴보도록 하자. Spring boot 는 기본적으로 transaction 대상의 aop를 동작시킬 프록시를 cglib 프록시를 사용하게 설정 해놨다. 이 뜻은 인터페이스가 있든 없든 상관없이 무조건 cglib를 사용해서 aop를 사용한다. 그렇다면 왜 이제와서 cglib를 기본적으로 사용할까? 필자의 추축컨데 예전의 비해 cglib가 좀 더 나아져서라고 생각할 수 밖에 없을 거 같다. 예전의 cglib는 안좋은 점이 몇가지가 있었다. 예를들어 final일 경우에는 cglib가 동작하지 않는다. 이것은 현재도 그렇다. 그리고 생성자가 두번 불리고 디폴트 생성자가 꼭 있어야만 했었다. 하지만 지금은 생성자가 한번 호출 되며 디폴트 생성자가 없어도 된다. 아 물론 그 생성자의 파라미터는 Bean으로 등록된 클래스만 가능하다. 이것은 objenesis 라이브러리를 사용하여 해결하였다. 하지만 이것은 spring 4.0 이후에만 해당사항이다. 아무튼 이렇게 예전 보다 좀 더 나아졌다. 그리고 성능 또한 jdk 프록시보다는 cglib가 빠르다. 성능테스트는 인터넷에 찾아보면 많기에 굳이 성능테스트는 하지 않겠다. 마지막으로 spring boot가 선택한 이유는 다음과 같다. Weve generally found cglib proxies less likely to cause unexpected cast exceptions. Spring boot 프로젝트 리더가 위와 같이 언급했다. 일반적으로 cglib가 예외를 발생시킬 가능성이 낮다는걸 발견하였다고 한다. 그래서 Spring boot의 transaction 대상 aop의 기본 전략을 cglib를 선택한 것으로 생각된다. 그러면 또 이렇게 생각할 수도 있겠다. 그럼 굳이 인터페이스를 만들 필요가 없지 않나?? 토비의 스프링 3.1 2권에 aop관한 이야기가 나온다.
인터페이스를 매번 정의하기가 귀찮은 개발자는 DI를 적용할 때도 대충 타깃 클래스를 사용하다가 프록시 AOP 처럼 DI 응용 기술이 필요한 경우에는 이를 이용하라는 뜻일까? 절대 그렇지 않다. 스프링은 여전히 인터페이스를 통한 객체지향적인 합성 기법과 이에 바탕에 둔 DI, 그리고 다양한 DI 활용방법을 사용하도록 권장한다. 프록시 AOP에서도 마찬가지이다. (이하생략).. 자바가 맴버필드를 public 으로 선언해 외부에서 직접 참조할 수 있게 했다고 해서 그것을 권장하는 게 아닌 것과 마찬가지다.
토비의 스프링에서는 위와 같이 설명하였다. (하지만 필자는 이상하게 인터페이스를 만들지 않는다..) 위의 말이 정확하고 맞는 말이다. 하지만 권장사항일뿐 각자가 융통성을 갖고 개발을 하면 될 것같다. Spring boot가 cglib를 선택한 이유를 알아봤으니 살짝 코드도 살펴보자. 일단 기본적으로 아무 설정 하지 않았을 경우에는 cglib를 선택한다. 그렇다면 만약 jdk 프록시를 사용하고 싶다면 어떻게 할까? 지금 현재 최신 버전인 Spring boot 1.5.3+ 인 경우에는 application.properties에 다음과 같이 설정하면 된다.
spring.aop.proxy-target-class=false
그러면 interface에 DI를 받으면 jdk 프록시가 DI된다. 하지만 이상황에서 직접적인 타깃 클래스를 DI받으면 에러가 발생한다. 그 이전 Spring boot 1.5.3 이전에는 위와 같이 했을 경우에는 동작하지 않는다. 설정 클래스에 다음과 같이 설정해줘야 한다.
@Configuration
@EnableTransactionManagement
public class Config {
  //..
}
근데 필자 생각에는 굳이 jdk 동적프록시를 만들 이유가 없어 보인다. 뭐 특별한 경우를 제외하고는 그냥 cglib 프록시를 이용해서 aop를 사용하면 될 것같다. 성능상 이점도 있고 예외 발생도 적다고하니 cglib를 권장할 뿐이다. jdk 프록시를 사용하고 싶다면 사용해도 된다. 이렇게 오랜만에 코드는 별로 없고 글만 쓴 포스팅이였다.