ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 지연 로딩과 조회 성능 최적화
    이커머스 devops 2025. 11. 20. 16:59

    주문 + 배송정보 + 회원을 조회하는 API

    1. 간단한 주문 조회 V2: 엔티티를 DTO로 변환

    // OrderSimpleApiController
    /**
     * V2. 엔티티를 조회해서 DTO로 변환(fetch join 사용X)
     * - 단점: 지연로딩으로 쿼리 N번 호출
    */
    @GetMapping("/api/v2/simple-orders")
    public List<SimpleOrderDto> ordersV2() {
        List<Order> orders = orderRepository.findAllByString(new OrderSearch());
        List<SimpleOrderDto> result = orders.stream()
             .map(o -> new SimpleOrderDto(o))
             .collect(toList());
        return result;
    }
    
    @Data
    static class SimpleOrderDto {
        private Long orderId;
        private String name;
        private LocalDateTime orderDate; //주문시간 
        private OrderStatus orderStatus;
        private Address address;
     
        public SimpleOrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
        }
    }
    • 쿼리가 총 1 + N + N번 실행된다
      • order 최초 1번
      • order -> member 지연로딩 조회 N번
      • oder -> delivery 지연로딩 조회 N번

     

     

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

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

     

     

    3. 간단한 주문 조회 V4: JPA에서 DTO로 바로 조회

    // OrderSimpleApiController
    private final OrderSimpleQueryRepository orderSimpleQueryRepository; //의존관계 주입
    
    /**
     * V4. JPA에서 DTO로 바로 조회
     * - 쿼리 1번 호출
     * - select 절에서 원하는 데이터만 선택해서 조회
    */
    @GetMapping("/api/v4/simple-orders")
    public List<OrderSimpleQueryDto> ordersV4() {
        return orderSimpleQueryRepository.findOrderDtos();
    }
    // OrderSimpleQueryRepository - 조회 전용 리포지토리
    @Repository
    @RequiredArgsConstructor
    public class OrderSimpleQueryRepository {
        private final EntityManager em;
        
        public List<OrderSimpleQueryDto> findOrderDtos() {
            return em.createQuery(
                    "select new 
                    jpabook.jpashop.repository.order.simplequery.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();
        }
    }
    // OrderSimpleQueryDto - 리포지토리에서 DTO 직접 조회
    @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;
        }
    }
    • SELECT 절에서 원하는 데이터를 직접 선택하므로 DB 애플리케이션 네트워크 용량 최적화
    • 리포지토리 재사용성 떨어짐

     

     

    [ 쿼리 방식 선택 권장 순서 ]

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

    '이커머스 devops' 카테고리의 다른 글

    OSIV와 성능 최적화  (0) 2025.11.21
    컬렉션 조회 최적화  (0) 2025.11.21
    Jenkins - CI/CD (1)  (0) 2025.11.03
    kafka (3)  (0) 2025.10.17
    kafka (2)  (0) 2025.10.17
Designed by Tistory.