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

API Specification을 명확하게 규정해야한다!

주문 조회 시 사용하는 DTO : SimpleOrderDto

@Data
static class SimpleOrderDto {
    private Long orderId;
    private String name;
    private LocalDateTime orderDate; //주문시간 private OrderStatus orderStatus;
    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();
    }
}
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2(){
    List<Order> orders = orderRepository.findAllByCriteria(new OrderSearch());
    //Order를 DTO로 변환!
    List<SimpleOrderDto> result = orders.stream()
            .map(o -> new SimpleOrderDto(o))
            .collect(Collectors.toList());
    return result;
}

람다식을 이용해서 리팩토링하면 다음처럼 코드 개선 가능 o->new SimpleOrderDto(o) : 생성자로 바로 넘기기 때문에

@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2(){
    return orderRepository.findAllByString(new OrderSearch)).stream()
            .map(SimpleOrderDto::new)
            .collect(toList());
}

V1, V2 문제점

지연 로딩으로 인해 DB 쿼리가 너무 많이 호출된다! V2 : Order, Member, Delivery 총 3개의 테이블 조회한다!

주문 조회 시 총 5번의 쿼리가 나간다! =>N+1 문제 발생‼️

  1. Order 조회 : 주문 결과 2건

  2. (루프1) DTO의 getMember().getName()로 MEMBER 조회

  3. DTO의 getDelivery().getAddress()로 DELIVERY 조회

  4. (루프2) DTO의 getMember().getName()로 MEMBER 조회

  5. DTO의 getDelivery().getAddress()로 DELIVERY 조회

첫번째 쿼리(Order 조회) 나가면 이로 인해 N번만큼 쿼리 추가 실행 Order 조회(1) + N(2) = 1 + 회원 N + 배송 N =>Order가 총 2개 : 1+ 2 + 2 = 5(최악의 경우)

지연로딩은 영속성 컨텍스트에서 조회하므로, 이미 조회된 경우 쿼리를 생략한다. 실제로 회원 아이디를 동일하게 하고 주문하면 1 + 1+ 1 + 0 + 1 = 4 =>총 4번의 쿼리가 나가는 것을 확인할 수 있다.

Last updated