#9 JPA 실무 - API 고급 - 지연 로딩과 조회 성능 최적화
Published on 25 Jun 2020
지연 로딩때문에 발생하는 성능 문제를 단계적으로 해결하는 방법
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 하위나 별도의 패키지로 차라리 분리해서 사용하는게 좋을 수도 있음
쿼리 방식 선택 권장 순서
- 엔티티를 DTO로 변환하는 방법을 선택한다.
- 필요하면 패치 조인으로 성능을 최적화 한다. -> 대부분의 성능 이슈가 해결됨.
- 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
- 최후의 방법은 JPA가 제공하는 Native SQL이나 Spring JDBC Template을 사용해서 SQL을 직접 사용한다.