Spring/API

API 개발 : 컬렉션 조회

JWonK 2022. 6. 28. 16:30
728x90
반응형

전에 했던 API 개발은 xToOne관계였다. 컬렉션이 아닌 단순한 형태의 조회였다.

이번에는 xToOne이 아닌 xToMany관계처럼 컬렉션 형태를 조회하는 것을 알아본다.

 

  • 주문 내역에서 추가로 주문한 상품 정보를 조회한다. 
  • 하나의 주문에 상품은 여러 개가 있을 수 있으므로 xToMany형태의 컬렉션 조회가 될 것이다.
  • 지금까지 게시글로 원하는 API 반환을 위해서는 API 스펙에 맞춰 DTO 클래스를 설계하여 반환해야한다는 것을 알고 있다는 가정 하, DTO로 변환하여 반환하는 코드로 작성
  • 조건 : API 반환으로 원하는 조건은 { 주문 번호, 사용자 이름, 주문 날짜, 주문 상태, 배송지 정보, 주문한 상품 정보} 이다. 이 스펙에 맞춰 DTO를 개발해야한다.

@RestController
@RequiredArgsConstructor
public class OrderApiController {

    private final OrderRepository orderRepository;

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

    @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().getUsername();
            orderDate = order.getOrderDate();
            orderStatus = order.getOrderStatus();
            address = order.getDelivery().getAddress();
            
            // 엔티티 그대로 가져오기 때문에 한 번 forEach로 변환          
            order.getOrderItems().forEach(o -> o.getItem().getName());
            orderItems = order.getOrderItems();
        }
        
    }
  }

지금까지 배웠던 단순 조회로 코드를 작성하면 이러한 형태가 될 것이다.

REST API Method 스펙은 GET에 맞춰 @GetMapping으로 하고 repository에서 받아온 엔티티를 API 스펙에 맞춘 DTO 형태로 변환한 후 이를 반환하는 방식이다. 

 

하지만 이렇게 하면 안된다. 반환하는 DTO 형태의 OrderDto 안에 orderItems를 한 번 보자. 주문이 가지고 있는 아이템 정보를 반환하는데 리스트 형태로 반환한다. 그럼 리스트가 이루고 있는 객체의 꼴은? OrderItem이다. 즉, 엔티티 자체로 반환해버린다.

 

DTO로 반환한다고 코드를 바꿨으나 DTO 안에 또 엔티티가 존재하는 꼴이 되어버린것이다.

이 조차도 모두 DTO로 변환해주어야한다.

 

@RestController
@RequiredArgsConstructor
public class OrderApiController {

    private final OrderRepository orderRepository;

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

    @Data
    static class OrderDto{

        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address;
        private List<OrderItemDto> orderItems;
        
        public OrderDto(Order order){
            orderId = order.getId();
            name = order.getMember().getUsername();
            orderDate = order.getOrderDate();
            orderStatus = order.getOrderStatus();
            address = order.getDelivery().getAddress();
          
            orderItems = order.getOrderItems().stream()
                    .map(orderItem -> new OrderItemDto(orderItem))
                    .collect(Collectors.toList());
        }
    }

    @Data
    static class OrderItemDto{
        /**
         * 개발 요구사항이 상품명, 주문가격, 주문수량만 요구했다는 가정.
         */
        private String itemName;
        private int orderPrice;
        private int count;
        
        public OrderItemDto(OrderItem orderItem){
            itemName = orderItem.getItem().getName();
            orderPrice = orderItem.getOrderPrice();
            count = orderItem.getCount();
        }
    }
}

위 코드에서 문제였던 부분을 다시 수정한 부분이다.

DTO 코드 안에서 완전히 엔티티를 의존하는 부분이 삭제되었다. 

 

실무에서는 절대 DTO가 엔티티를 의존하는 코드를 작성하면 안된다. API 스펙에 맞춘 DTO 자체로 개발을 진행해야한다.

 

이렇게 되면, 엔티티를 API로 노출시키는 문제는 해결되었다.

하지만 원하는 정보를 얻기 위해 각각의 테이블로 쿼리문을 날리는 아주 비효율적인 코드이다.

이를 최적화하는 방법에 대해 다음 게시글로 정리하고자 한다.

 

※ 반환할 때는 좀 더 유연한 API 개발을 위해 Result 클래스로 반환해주어야한다.

https://wonsjung.tistory.com/421

 

회원 조회 API 개발 / Result 클래스로 유연한 JSON 반환

회원 조회는 값을 가져와 화면에 보여주기만 하면 된다. 즉, 생성 / 수정 없이 조회만 하면 된다 -> REST API : GET Method 사용 회원 조회 V1 : 응답 값으로 엔티티를 직접 외부에 노출 @RestController @Require

wonsjung.tistory.com

 

728x90
반응형