간단한 주문 조회 V1: 엔티티를 직접 노출
주문 + 배송정보 + 회원을 조회하는 API를 만들어본다! 한마디로 주문 API를 만든다.
핵심은 지연 로딩 때문에 발생하는 성능 문제를 해결하는 것이다!
SQL로 해결하기 어려운 성능 문제를 JPA를 통해 손쉽게 튜닝해서 성능 최적화를 이룬다!⭐️⭐️⭐️ 지금부터 설명하는 내용은 정말 중요합니다. 실무에서 JPA를 사용하려면 100% 이해해야 합니다.
엔티티 직접 노출한 주문조회 결과
첫번째 문제 : 무한루프 발생 양방향 순환 참조로 인해 무한루프가 발생한다!
아래 ordersV1()의 반환타입은 Order 컬렉션이다.
Order에는 Member가 있고 JSON으로 Member를 뿌려야 한다.
Member 에는 또 List<Order>가 있다.
=>반복. 즉, 무한 루프에 빠지게 된다! 마치 양방향 관계의 tostring()처럼
해결 방법
양방향 관계에서 어느 한쪽 필드에 JSON Jackson 라이브러리의 @JsonIgnore 해주어야한다!⭐️⭐️⭐️ =>Order에서 다른 엔티티로 갔다가 다시 Order로 돌아가는 모든 엔티티(클래스)의 필드에 @JsonIgnore!!!
Order->Member : Membe의 Order 필드에 @JsonIgnore Order->List<OrderItem> : OrderItem의 Order 필드에 @JsonIgnore Order->Delivery : Delivery의 Order 필드에 @JsonIgnore
두번째 문제 : 첫번째 문제 해결 후
Order 클래스 내의 Member (Order->Member) @ManyToOne(fetch = LAZY) : 지연 로딩 =>진짜 엔티티가 아니라 프록시 객체를 가져온다! Member 객체를 상속받는 프록시 객체를 담아둔다!(프록시 기술 쓸 때 bytebuddy라는 라이브러리 많이 사용) (Member 객체를 사용하려고 접근할 때(=프록시 초기화) 진짜 엔티티를 가져온다!)
문제는 JSON Jackson 라이브러리가 이 Member 객체를 가져오려고 하는데 진짜 엔티티가 아니라 프록시 객체이다!(그 객체를 사용하지 않고 조회만 하기 때문에 진짜 엔티티가 아니라 프록시 객체인 것이다!)
해결 방법1 : Hibernate5Module 이용 Hibernate에서 JSON으로 프록시 객체 같은 것들은 처리하지 않도록 설정할 수 있다!(모듈 설치 필요) build.gradle에 추가 : 버전은 따로 추가하지 않아도 스프링에서 최적의 버전을 자동으로 가져온다!
com.fasterxml.jackson.datatype:jackson-datatype-hibernate5
Order 조회 시 Member, Delivery : xToOne(fetchType = LAZY)이기 때문에 Order만 조회. => 지연로딩은 아직 DB에서 조회한 게 아니기 때문에 Hibernate는 Order 조회 시에 Member, Delivery는 JSON에서 null로 처리한다!
해결 방법1-1 FORCE_LAZYLOADING : LAZY로 된 애들 데이터들 다 로딩(조회)한다! 사용❌ "강제로 LAZY 로딩을 해버린다" = "강제로 LAZY 로딩을 무시(초기화)하고, EAGER처럼 로딩해버린다"
문제점 : 엔티티를 그대로 모두노출한다!!! 엔티티를 그 자체를 모두 노출하는 문제점, 성능 저하 문제점이 발생한다‼️ 애초에 필요한 데이터인 Member, Delivery 뿐만 아니라 불필요한 데이터들까지 조회해버리기 때문에 결국 성능상 문제도 발생한다!!!
해결 방법 2 Order의 Member, Delivery 객체(프록시)에 아무 메서드를 호출해서 원하는 애들만 선별적으로 프록시 객체 초기화! order.getMember()로 프록시 객체를 가져오고, order.getMember().getName()으로 진짜 엔티티를 사용(접근)함으로써 진짜 엔티티를 가져올 수 있다. =>LAZY 지연 로딩이 강제 초기화된다!!!!! =>프록시 객체를 강제로 초기화함으로써 LAZY 지연 로딩은 강제로 초기화된다!
문제점 : 조회 결과 MEMBER, DELIVERY 테이블의 모든 데이터들이 다 조회된다! 보통 회원명과 배송상태 정도만 조회한다. 하지만 이런식으로 테이블의 모든 데이터들이 API로 다 노출이 된다면 운영이 어렵고, 이미 어떤 클라이언트에서 이 데이터를 사용하고 있다면 수정, 변경도 어렵다! =>그래서 필요한 데이터만!!!!! DTO로 만들어서!!!! 사용해야 한다!!!!!⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
해결방법 3 : LAZY fetchType을 EAGER로 바꾸기 LAZY 지연로딩으로 된것을 EAGER로 바꾸면 위의 코드에서 for문을 없앨 수 있다.
문제점 => 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 데이터를 항상 조회해서 성능 문제가 발생할 수 있다‼️ => 즉시 로딩으로 설정하면 성능 튜닝이 매우 어려워 진다.
정리
지연로딩을 피하기 위해 즉시 로딩(EARGR)으로 설정❌ 항상 지연 로딩을 기본으로 하고, 성능 최적화가 필요한 경우에는 페치 조인(fetch join)을 사용해라!(V3 에서 설명)
Last updated