Spring을 이용한 API 개발, 지연로딩과 조회 성능 최적화

4235 단어 SpringSpring

1. Entity -> DTO로 변환해서 조회

 @GetMapping("/api/v2/simple-orders")
 public List<SimpleOrderDto> ordersV2() {
 	
 	List<Order> orders = orderRepository.findAll();
 	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();
        }
}

일단 Entity를 바로 반환 하는 것은 아주 좋지않다. 최소한 DTO로 변환해서 반환하자.

DTO내부에 Collection이 있는 경우 쿼리가 총 1+N번 실행된다 -> N+1 문제 발생

2. Fetch join 최적화

Controller

@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;
}

Repository

//Repository 추가 코드
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();
}

Entity를 페치 조인(fetch join)을 사용해서 쿼리 1번에 모든 결과를 조회한다.
페치 조인으로 order-> member, order->delivery는 이미 조회된 상태이므로 지연로딩이 발생하지 않는다.

3. JPA에서 DTO 바로 조회

Controller

@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> ordersV4() {
      return orderSimpleQueryRepository.findOrderDtos();
}

Repository

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();
}

일반적인 SQL을 사용할 때 처럼 원하는 값들을 선택해서 조회한다.
Select절에서 원하는 데이터를 직접 선택하기 때문에 네트워크 용량 최적화(생각보다는 미비함..)
Repo의 재사용성이 떨어진다. API스팩에 맞춘 코드가 Repo에 들어가는게 단점...

결론은?

  1. Entity를 DTO로 변환해서 반환한다.
  2. 필요하다면 fetch join으로 성능을 최적화한다. (대부분의 경우 여기서 해결!)
  3. 그래도 안되면 JPA에서 DTO를 직접 조회한다.
  4. 최후의 방법으로 JPA가 제공하는 네이티브SQL이나 스프링 JDBC Template을 사용하여 직접 SQL을 사용한다.

출처: https://www.inflearn.com/users/@yh

좋은 웹페이지 즐겨찾기