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

no properties이슈 트러블 슈팅 원인 : getter/setter 가 없음 해결방법 : DTO 클래스에 @Data 또는 @Getter를 추가한다. DTO라면 @Data로 해도 된다 하지만 실무에서는 @Data가 워낙 많아서 애매한 경우는 안 쓰는 게 나을 수도 있다.

DTO(OrderDto) 생성

  1. DTO 생성

  2. 필요한 데이터 정의

  3. 생성자 생성

DTO 생성
DTO를 반환하는 모습

orderItems는 "엔티티"이기 때문에 null이 나왔다. V1에서처럼 stream으로 반복문 돌려서 출력할 수도 있지만 좋은 방법이 아니다. stream으로 돌려서 주문 상품의 상품명을 출력하는 코드 : 주문 상품 컬렉션에서 각 객체의 이름 조회

List<OrderItem> orderItems = order.getOrderItems();
orderItems.stream().forEach(o -> o.getItem().getName());

프록시 객체 강제 초기화 : public OrderDto에서 order.getOrderItem().stream().forEach(o -> o.getItem().getName())

@Data
static class OrderDto{
    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;
    private List<OrderItem> orderItems;

    public OrderDto(Order order){//생성자는 왜 있는 거지? =>생성자 주입!
        orderId = order.getId();
        name = order.getMember().getName();
        orderDate = order.getOrderDate();
        orderStatus = order.getStatus();
        address = order.getDelivery().getAddress();
        order.getOrderItems().stream().forEach(o -> o.getItem().getName());//프록시 강제 초기화
        orderItems = order.getOrderItems();
    }
}
프록시 객체 강제초기화 후

우리가 만든 DTO(OrderrDto) 안에는 엔티티가 있는데, DTO 안에는 엔티티가 있으면 안 된다! 엔티티가 외부에 노출되기 때문이다! 엔티티는 외부에 노출되면 안 되는데 이것을 DTO로 만든다고 해도 엔티티는 그대로 노출된다. 엔티티 의존성을 모두 없애야 한다! =>단순히 DTO로 만드는 것이 아니라, DTO 내부에서 엔티티가 있으면 안 된다!!! (Address 같은 Value Object는 가능)

그래서 List<OrderItem> 도 모두 DTO로 바꿔야 한다! 그렇지 않으면 추후에 OrderItem 엔티티 수정하게 된다면 API도 다 바뀐다!! =>statlc class OrderItemDto {...} 생성 필요!

OrderDto의 내부에 OrderItem도 마찬가지로 DTO 생성, 필요한 데이터 정의, 생성자 생성 (참고 : 여전히 배열 타입으로 반환하고 있지만 배열 타입으로 반환❌)

코드 정리 1. OrderRepository에서 List<Order> 엔티티 조회 : 실무에서는 페이징. 2. 반복문 돌리면서 OrderDto로 변환. 3. OrderDto 내에서도 List<OrderItem> 또한 DTO로 생성해서 DTO로 반환한다! => OrderItemDTO 생성해서 원하는 데이터들 정의하고 리턴

@GetMapping("/api/v2/orders")
public List<OrderDto> ordersV2() {
    List<Order> orders = orderRepository.findAllByCriteria(new OrderSearch());
    List<OrderDto> collect = orders.stream()
            .map(o -> new OrderDto(o))
            .collect(Collectors.toList());
    return collect;
}

SQL문 분석 SQL은 얼마나 실행될까?

N+1문제

  1. Order 엔티티(List<Order>) 조회 : 2건 조회

  2. 각 Order 객체는 반복문 돈다. 1. Member 조회 2. Delivery 조회 3. List<OrderItem> 조회 : 각 회원별로 주문한 상품 갯수만큼 반복. 여기서는 2개 orderItem 조회

=>한번의 주문 조회로 5*2 + 1 = 11개의 쿼리가 발생하는 것을 알 수 있다.

N+1문제 Order 조회 시, 2건의 Order 존재.

첫번째 Order부터 반복문 돈다. - Member, Address, List<OrderItem> 가져온다. List<OrderItem> 모두 지연 로딩.

=>컬렉션으로 일대다 관계에서 테이블을 조회하면 N+1 문제 발생!

Last updated

Was this helpful?