실전! 스프링 부트와 JPA 활용2(API 개발과 성능 최적화)

Ch03. API 개발 고급(지연 로딩과 조회 성능 최적화) - 간단한 주문 조회 V1(엔티티를 직접 노출)

webmaster 2021. 12. 20. 10:55
728x90

주문 + 배송정보 + 회원을 조회하는 API를 만들자

참고: 지금부터 설명하는 내용은 정말 중요합니다. 실무에서 JPA를 사용하려면 100% 이해해야 합니다. 안그러면 엄청난 시간을 날리고 강사를 원망하면서 인생을 허비하게 됩니다.

지연 로딩 때문에 발생하는 성능 문제를 단계적으로 해결해보자

OrderSimpleApiControllerV1 -> 엔티티 그대로 노출

  • 무한루프 발생
  • Why? -> order - Member를 순환참조한다.
  • 해결하기 위해 양방향 연관관계가 있는 앤티티를 JsonIgnore 해준다.

 Member,delivery,orderItem

Example) OrderItem.order 양방향 JsonIgnore

  • 에러 발생 500 ->org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor];
  • Why? 지연로딩이기 때문에 실제 DB에서 데이터를 가지고 오는것이 아니라, 프록시객체를 생성해 넣어준다.(byteBudy가 프록시객체이다)
    • Json Lib에서 해당객체를 어떻게 할수가 없기 때문에 에러를 발생시킨것이다.
    • hibernate한테 해당지연로딩 객체를 뿌리지 말라고 해야된다.

해결

1. build.gradle에 추가

의존성 추가

2. Bean 추가

Hibernate5Module 빈으로 등록

  • 에러가 해결되지만 성능상 좋지 않다.
  • 엔티티를 그대로 노출하기 때문에 내가 필요하지 않는 필드를 조회해 오는 쿼리와, 앤티티를 변경하면 Api 스팩이 변경되는 문제가 발생한다.

프록시를 강제 초기화 하여 에러 해결 가능

Lazy 강제로 초기화하여 에러를 해결할 수 있다.

  • 주의: 엔티티를 직접 노출할 때는 양방향 연관관계가 걸린 곳은 꼭! 한곳을 @JsonIgnore 처리 해야 한다. 안그러면 양쪽을 서로 호출하면서 무한 루프가 걸린다.
  • 참고: 앞에서 계속 강조했듯이 정말 간단한 애플리케이션이 아니면 엔티티를 API 응답으로 외부로 노출하는 것은 좋지 않다. 따라서 Hibernate5Module 를 사용하기 보다는 DTO로 변환해서 반환하는 것이 더 좋은 방법이다. 
  • 주의: 지연 로딩(LAZY)을 피하기 위해 즉시 로딩(EARGR)으로 설정하면 안된다! 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 데이터를 항상 조회해서 성능 문제가 발생할 수 있다. 즉시 로딩으로 설정하면 성능 튜닝이 매우 어려워 진다. 
  • 항상 지연 로딩을 기본으로 하고, 성능 최적화가 필요한 경우에는 페치 조인(fetch join)을 사용해라!(V3 에서 설명)
728x90