[스프링 활용2] 간단한 주문 조회 V2 : 엔티티를 DTO로 반환

atdawn·2024년 8월 17일

SPRING BOOT+JPA

목록 보기
40/49

참고 : [실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화] - 김영한


이번에는 엔티티 직접 노출이 아닌 정석 방법으로 DTO로 변환하여 반환해보자!

OrderSimpleApiController

...
/**
* V2. 엔티티를 조회해서 DTO로 변환(fetch join 사용X) * - 단점: 지연로딩으로 쿼리 N번 호출
*/
 @GetMapping("api/v2/simple-orders")
    public List<SimpleOrderDto> ordersV2(){ //원래는 List로 바로 반환 X. result로 감싸야 함.
        List<Order> orders = orderRepository.findAllByString(new OrderSearch());
        List<SimpleOrderDto> result = orders.stream()
                .map(o->new SimpleOrderDto(o))
                .collect(Collectors.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();  //LAZY 초기화
            orderDate=order.getOrderDate();
            orderStatus=order.getStatus();
            address=order.getDelivery().getAddress();  //LAZY 초기화
        }
    }
...
  • orderRepository.findAllByString(new OrderSearch())를 통해 모든 주문 목록을 조회

  • 조회된 Order 엔티티 리스트(orders)를 SimpleOrderDto로 변환

  • 변환된 SimpleOrderDto 리스트를 클라이언트에게 반환

  • SimpleOrderDto

    • Order 엔티티에서 필요한 정보만을 포함하는 DTO
    • 이를 통해 클라이언트에 불필요한 정보가 전달되지 않으며, 엔티티 변경 시 API 스펙이 변경되는 것을 방지

  • V1은 엔티티를 바로 반환하기 때문에 엔티티 이름 그대로 나오게 된다. (V2의 orderId가 V1에서는 id로 나옴)

가급적으로는 이처럼 DTO로 바꿔서 반환해야한다!


그러나

  • 쿼리가 총 1 + N + N번 실행된다. (v1과 쿼리수 결과는 같다.)
  • order 조회 1번(order 조회 결과 수가 N이 된다.)
  • order -> member 지연 로딩 조회 N 번
  • order -> delivery 지연 로딩 조회 N 번
  • 예) order의 결과가 4개면 최악의 경우 1 + 4 + 4번 실행된다.(최악의 경우)
    • 지연로딩은 영속성 컨텍스트에서 조회하므로, 이미 조회된 경우 쿼리를 생략한다.

간단한 쿼리일 경우 괜찮을 수 있지만,
N+1 문제가 발생한다.

해결 방법:

  • 페치 조인(Fetch Join)
    • JPA에서는 이 문제를 해결하기 위해 페치 조인을 사용.
    • 페치 조인은 연관된 엔티티를 한 번의 쿼리로 함께 가져오는 방법으로, 이를 통해 N+1 문제를 해결.
profile
복습 복습 복습

0개의 댓글