-
지연 로딩과 조회 성능 최적화이커머스 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 - 쿼리가 총 1 + N + N번 실행된다