[ORM 표준 JPA] 15장 프록시 심화

2021. 8. 30. 00:09개발공부/JPA 스터디

728x90

어느새 스터디가 한 주를 남기고 있습니다. 넥스트 스텝 교육을 병행하기가 이렇게 힘들줄은 생각못한 자신에게 반성시키고 싶지만 그래도 고생한만큼 마무리까지 잘하도록 하겠습니다.

JPA 프록시 심화

우선 프록시는 실제 DB에 접근하지 않아도 된다는 성능에 있어 강점을 가진 엔티티 호출 방식입니다. JPA는 지연 로딩을 통해 원본 엔티티 대신 프록시를 참조하며 프록시는 원본 엔티티를 상속받아 객체의 참조를 보관하고 있습니다. 때문에 메서드 호출을 프록시로 할 경우 프록시는 실제 엔티티에 호출을 전달하게 됩니다.

프록시 테스트 중 member 엔티티에서 Spring Data JPA 프록시 호출 메서드인 getById를 사용해도 프록시가 생성되지 않는 오류가 있었는데 원인은 엔티티 구조상 있는 것으로 보여서 찾고 있습니다. team 엔티티로 테스트하면 잘되니 엔티티 문제인 것 같습니다.

문제 원인은 2가지 였고 엔티티 구조에는 아무런 문제가 없었습니다.

1) Proxy 검증 Syntax 오류

java.lang.reflect.Proxy의 isProxyClass(Class) 메서드는 동적 프록시 인터페이스를 상속하는 클래스인지 여부를 확인하기 때문에 테스트 중인 객체는 프록시로 판단하여 True를 반환한 것으로 판단됩니다.(확실친 않음) 다만 확실한 것은 JPA 상 객체 프록시 여부 검증을 위해선 아래와 같은 엔티티를 상속한 인터페이스 Proxy인 HibernateProxy 클래스를 통해 검증해야 합니다.


  Member refMember = memberRepository.getById(1L);
  Member findMember = memberRepository.findById(1L).orElseThrow(Exception::new);
  if (refTeam instanceof HibernateProxy) {
      //...
  }

2) getClass() 사용하지 않고 객체를 출력한 오류

클래스가 프록시일 때 refMember = class learn.jpa.model.ch10.Team$HibernateProxy$7SA6mGpn 형식의 클래스를 반환해줍니다. 하지만 getClass를 사용하지 않고 출력하면 프록시인지 알 수 없습니다.

  //출력결과
  refMember is proxy: true
  //일반 Object 출력
  refMember = learn.jpa.model.ch10.Team@2c8a445b
  //getClass() 출력
  findMember = class learn.jpa.model.ch10.Team$HibernateProxy$zLqsHUQk

3) @Modifying 오용

@Query 어노테이션으로 쿼리를 작성한 메서드에만 @Modifying을 이용해 (flushAutomatically, clearAutomatically)을 설정할 수 있습니다. 이번 코드는 springframework.data 에 내장된 save() 쿼리 메서드를 사용했기 때문에 수동으로 entityManager 객체의 clear() 메서드를 사용해 영속성 컨텍스트를 초기화 시켜줬어야 했습니다.

 

프록시 동등성 비교부터 이해가 잘 가지 않았는데, 스터디하면서 알게 된 것은 프록시 패턴이나 N+1을 겪은 상태에서 알아두면 좋은 개념이라는 사실이었습니다. 개인적 견해니 참고하시면 좋을 것 같습니다. 그리고 APM 성능 분석과 함께 써야하는데 그 이유는 프록시가 잘못 쓰이고 있을 경우 성능이 저하될 가능성이 있기 때문입니다.

참고로 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환합니다.

정리

member 엔티티 프록시 생성 오류는 파악되는대로 업데이트 하겠습니다. 역시 배운것은 사용해봐야 체득이 되는 것 같습니다.


참고자료: 자바 ORM 표준 JPA 프로그래밍 (김영한 저)