Avatar 창조적이고 생산적이고 싶은 개발자

#9 JPA 실무 - API 고급 - 지연 로딩과 조회 성능 최적화

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

xToOne

Entity를 직접 api에서 노출의 문제

  • 양방향 연관관계는 순환 참조로 인해서 무한하게 데이터를 전송하게 된다
    • @JsonIgnore를 Entity에 다 붙여줘야 한다…
  • 양방향에 대해서 처리한다고 해도 Lazy로드 인 경우 Proxy객체(ByteBuddyInterceptor)를 mapping하려고 하다가 타입을 찾을 수 없어 오류가 발생한다.
    • hibernate5module(implementation ‘com.fasterxml.jackson.datatype:jackson-datatype-hibernate5’ )을 등록해서 Lazy인 경우 가져오지 않도록 할 수 있지만 어차피 Entity를 직접 노출하는 것 자체가 좋은 방법이 아니다
  • 지연로딩을 피하려 EAGER 로딩을 하려고하면 더더욱 느려지고 필요 없는 곳에서도 참조하게되어서 성능 최적화의 여지가 더 적어진다.

api를 대신에 DTO를 노출

  • Entity가 변경되었을 때도 api 스펙을 바꿀 필요가 없음.
  • 값 타입을 좀 더 참고해볼 것
  • 이것도 마찬가지로 Lazy 로딩이 걸린 관계가 있다면 마찬가지로 성능 이슈가 발생할 수 있음

Lazy로딩 메커니즘

  • ORDER -> SQL 1번 -> Member 2번 + delivery 2번
    • N + 1 -> 1 + 회원 N + 배송 N -> 5번
  • Lazy로딩은 영속성 컨텍스트에서 관리하므로 조회된 경우 영속성 컨텍스트에서 찾아오므로 중복 SQL은 없어진다.

EAGER를 사용하면?

  • 쿼리가 어떻게 날아가는지 예측하기가 힘들고 최적화된 성능을 이끌어내기 힘들다

fetch join

  • fetch join 을 활용하면 내부적으로 join query로 변경되면서 엔티티 그래프를 묶어서 한꺼번에 가져올 수 있음
  • 성능적으로 가장 좋음
  • 무조건 100% 알아놔야 함

jpql에 dto를 반환하도록 하면?

  • 쿼리가 간결해짐 -> 네트워크 용량을 최적화 할 수 있음
    • 다만 큰 차이는 없음. 요즘은 네트워크 짱좋음
    • 정말 대용량 트래픽이면 고민해볼만한 문제
  • dto를 변환하는 코드를 따로 작성할 필요가 없음
  • 재사용성이 떨어지고 repository에서 api 스펙을 정의해버려서 영역을 침범하게 됨
    • 물리적으로는 계층을 나눌 수 있다고 하더라고 논리적으로 의존성이 생겨버리게 된다.
    • repository 하위나 별도의 패키지로 차라리 분리해서 사용하는게 좋을 수도 있음

쿼리 방식 선택 권장 순서

  1. 엔티티를 DTO로 변환하는 방법을 선택한다.
  2. 필요하면 패치 조인으로 성능을 최적화 한다. -> 대부분의 성능 이슈가 해결됨.
  3. 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
  4. 최후의 방법은 JPA가 제공하는 Native SQL이나 Spring JDBC Template을 사용해서 SQL을 직접 사용한다.