Entity -> DTO로 변환해서 반환
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2(){
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<SimpleOrderDto> result = orders.stream()
.map( o -> new SimpleOrderDto(o))
.collect(Collectors.toList());
return result;
}
DTO의 모습
@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로 변환해서 반환하는게 일반적인 모습이다
하지만 이러한 코드에서도 저번코드와 같이 에러는 발생하지않지만 안좋은 성능을 보여준다.

단순히 order조회를 할뿐인데 다음과 같은 쿼리가 여러번 나가게 된다. (N+1 문제 발생)
1 + N + N 번 실행이 된다.
Order 조회 1번
order -> member 지연 로딩 조회 N번
order -> delivery 지연 로딩 조회 N번
지연로딩은 영속성 컨텍스트에서 조회하기에 이미 조회된 경우 쿼리를 생략하지만
우리는 성능을 운에 맡길 수 없다.
이러한 쿼리를 한번에 조회할 수 없을까 ? 보기만 해도 성능이 안좋아 보인다.
Entity -> DTO - fetch join
우리는 많은 쿼리의 양을 fetch을 사용해서 쿼리의 양을 줄일 수 있다.
@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> orderV3(){
List<Order> orders = orderRepository.findAllWithMemberDelivery();
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o))
.collect(Collectors.toList());
return result;
}
위에와 비슷하지만 orderRepository에 새로운 메소드를 만들어 주었다
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();
}
다음과 같은 메소드를 만들어 주었는데
쿼리 문을 통해 fetch 을 사용해서 Order을 조회할때 member, delivery도 같이 조회해서 가져오는 로직을 작성 했다.

따라서 길지만 여러개의 쿼리를 날리지않고 1개의 쿼리로 조회가 가능하다.
패치 조인을 사용하면 성능 최적화를 할 수 있으니 잘 알아 두자
이렇게 해도 문제는 없지만 필요하지 않는 필드 까지도 한번에 가져오는 모습을 보여준다.
따라서 필요한 필드만 가져오려면 어떻게 해야할까
JPA에서 DTO 바로 조회 == Entity -> DTO - fetch join 필요한 필드만 가져오기
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> orderV4(){
return orderRepository.findOrderDtos();
}
public List<OrderSimpleQueryDto> findOrderDtos() {
return em.createQuery(
"select new jpabook.jpashop.repository.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();
}

위와 다른 모습으로 필요한 필드만 가져오는 모습을 보여주고 있다.
new 명령어로 JPQL의 결과를 즉시 DTO로 변환한다
사실 마지막인 JPA 에서 DTO로 바로 조회하는 방법이 좋아 보이지만 안좋은 방법 도 존재한다.
아래서 확인하자
엔티티 -> DTO VS DTO로 바로 조회
엔티티 -> DTO : 엔티티조회후 DTO로 변경해준다
DTO로 바로 조회 : new 명령어로 처음부터(repository에서) Dto로 넣어서 반환한다
사실이 2가지 방법 모두 사용해도 되는데 이런게 트레이드 오프이다.
성능상으로만 본다면 필요한 필드만 조회하는 DTO 바로 조회 방법이 좋지만
생각보다 이런 네트웍 용량 최적화를 하지만 용량은 큰게 아니라면 생각보다 미비하지만 성능이 좋아지고
엔티티 -> DTO를 사용하게 된다면 성능은 살짝 떨어지지만 재사용할 수 있는 코드가 증가하게 된다
생각보다 성능이 크지않아 엔티티 -> DTO 방법을 사용하자
쿼리 방식 선택 권장 순서
1. 우선 Entity -> DTO로 변환하는 방법 선택
2.필요하다면 fetch join으로 성능을 최적하 -> 대부분 이슈 해결된다.
3.그래도 안된다면 DTO로 직접 조회하는 방법을 사용한다 -> 성능 개선 new 연산자 사용
4. 마지막 방법 JPA 가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.
'Spring' 카테고리의 다른 글
[Spring] 컬렉션 조회 최적화 방향성 (0) | 2024.01.06 |
---|---|
[Spring] 컬렉션 Entity -> DTO 성능최적화 (1) | 2024.01.05 |
[Spring] Entity를 직접 노출 안하는 이유 (0) | 2024.01.03 |
[Spring] @RequestBody HTTP 메시지 바디 처리하는 어노테이션 (0) | 2023.08.18 |
[Spring] @RequestParam 요청 파라미터 데이터 읽어오기 (0) | 2023.08.17 |