[JPA 활용2] API 개발 고급 - 지연 로딩과 조회 성능 최적화 ②

[ 간단한 주문 조회 V3: 엔티티를 DTO로 변환 - 페치 조인 최적화 ]

1. OrderSimpleApiController - V3 추가

/**
* V3. 엔티티를 조회해서 DTO로 변환 (fetch join 사용 O)
* - fetch join으로 쿼리 1번 호출
*/
@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> orderV3() {
    List<Order> orders = orderRepository.findAllWithMemberDelivery();
    List<SimpleOrderDto> result = orders.stream()
        .map(o -> new SimpleOrderDto(o))
        .collect(toList());
    return result;
}    

2. OrderRepositoryfindAllWithMemberDelivery 추가

public List<Order> findAllWithMemberDelivery() {
    return em.createQuery(
        "select o from Order o" +
            " join fetch o.member m" +
            " join fetch o.delivery d", Order.class)
        .getResultList();
}
  • 엔티티를 페치 조인(fetch join)을 사용해서 쿼리 1번에 조회한다.
  • 페치 조인으로 order → member, order → delivery는 이미 조회된 상태이므로 지연로딩 X

실행 결과

  • V2는 쿼리가 5번 나갔는데, V3는 쿼리가 딱 한 번만 나간다!

[ 간단한 주문 조회 V4: JPA에서 DTO로 바로 조회 ]

1. OrderSimpleApiController - V4 추가

/**
* V4. JPA에서 DTO로 바로 조회
* - 쿼리 1번 호출
* - select 절에서 원하는 데이터만 선택해서 조회
*/
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> orderV4() {
    return orderSimpleQueryRepository.findOrderDtos();
}

2. OrderSimpleQueryRepository 생성

@Repository
@RequiredArgsConstructor
public class OrderSimpleQueryRepository {

    private final EntityManager em;

    public List<OrderSimpleQueryDto> findOrderDtos() {
        return em.createQuery(
                "select new jpabook.jpashop.repository.order.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" +
                        " from Order o" +
                        " join o.member m" +
                        " join o.delivery d", OrderSimpleQueryDto.class)
                .getResultList();
    }
}
  • new를 이용해서 객체를 생성할 때 full package path를 입력해야 한다.

3. OrderSimpleQueryDto 생성

@Data
public class OrderSimpleQueryDto {

    private Long orderId;
    private String name;
    private LocalDateTime orderDate;    //주문 시간
    private OrderStatus orderStatus;
    private Address address;

    public OrderSimpleQueryDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus, Address address) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
    }
}
  • 일반적인 SQL을 사용할 때처럼 원하는 값을 선택해서 조회한다.
  • new 명령어를 사용해서 JPQL의 결과를 DTO로 즉시 변환한다.
  • SELECT 절에서 원하는 데이터를 직접 선택하므로 DB → 애플리케이션 네트워크 용량 최적화! (생각보다 미비)
  • 리포지토리 재사용성이 떨어진다.

실행 결과

📌 정리

엔티티를 DTO로 변환하거나(V3), DTO로 바로 조회하는 방법(V4)은 각각 장단점이 있다. 그래서 사실 우열을 가리기는 힘들다!
V3보다 V4가 성능 최적화는 되었지만, 재사용성이 좋지 않다.

💡 쿼리 방식 선택 권장 순서

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

좋은 웹페이지 즐겨찾기