본문 바로가기
Back-end/JPA 개념

API 조회 성능 최적화 - DTO 직접 조회

by devraphy 2022. 5. 30.

0. 개요

- 일반적으로 JPA에서 조회를 수행할 때, Entity로부터 조회된 결과를 DTO를 통해 전달한다.

- 그러나 이러한 과정을 스킵하고 바로 DTO를 통해 조회할 수 있는 방법은 없을까? 

- 이번 포스팅에서는 DTO를 통해 직접 조회 방법에 대해 알아보자. 

 

 

1. DTO를 통한 직접 조회

- 이전 포스팅에서는 Entity의 조회 결과를 DTO에 넘겨서 DTO 객체를 반환하는 방법으로 조회하였다.

- DTO를 통한 직접 조회 방법은 조회 결과를 DTO 객체로 직접 반환하는 방법이다. 

 

 

a) DTO 클래스 생성

- 다음과 같이 DTO 클래스를 생성한다. 

@Data
public class SimpleOrderQueryDto {
    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;

    public SimpleOrderQueryDto(Long orderId, String name, LocalDateTime orderDate,
                               OrderStatus orderStatus, Address address) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
    }
}

 

- 위의 코드에서 주목해야 할 부분은 생성자의 매개변수다.

- 이전 포스팅에서 Entity를 거쳐 DTO로 변환하는 방식에서는 매개변수에 Entity 객체가 들어갔다.

- 그러나 DTO를 통한 직접 조회 방식은 위의 생성자처럼 모든 요소를 매개변수로 받는다.

 

 

b) Repository에 조회 메서드 생성

- 필드를 매개변수로 받는 이유는 Repository의 조회 메서드 내부에서 사용하기 위함이다.

- 다음 Repository의 조회 메서드를 살펴보자.  

@Repository
@RequiredArgsConstructor
public class OrderRepository {
    private final EntityManager em;

    public List<SimpleOrderQueryDto> findAllUsingDto() {
        return em.createQuery(
             "select new jpabook" +
             ".jpabook.api" +
             ".SimpleOrderQueryDto(o.id, m.name, o.orderDate, o.status, d.adderss)" +
             " from Order o" +
             " join o.member m" +
             " join o.delivery d", SimpleOrderQueryDto.class).getResultList();
    }
}

 

- 위의 메서드 내부의 JPQL을 주목하자.

- DTO를 통해 직접 조회하기 위해서 JPQL에서 new 연산자를 이용해 DTO 객체를 생성하였다.

- 그리고 매개변수로 조회된 Entity의 요소가 들어간다.

- 이 과정을 위해 DTO 생성자에서 객체가 아닌 필드를 매개변수로 받은 것이다. 

    → 이전 포스팅에서도 언급한 내용으로, JPQL에서 객체는 기본 키를 의미한다. (https://devraphy.tistory.com/604)

 

- Controller에서 다음 메서드를 생성하고 API를 호출해보자. 

@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {

    private final OrderRepository orderRepository;

    @GetMapping("api/v4/simple-orders")
    public List<SimpleOrderQueryDto> orderV4() { // DTO를 이용한 직접 조회
        return orderRepository.findAllUsingDto();
    }
}

 

 

c) 실행 결과

- 콘솔을 확인하면 다음과 같은 쿼리가 발행한다. 

 

- 이전 포스팅의 실행 결과와는 다르게 select 부분이 굉장히 깔끔한 것을 확인할 수 있다.

- 이처럼 select 부분이 깔끔한 이유는 필요한 데이터를 직접 선택하였기 때문이다.

 

 

2. DTO 변환 VS DTO 직접 조회

- 이전 포스팅에서 배웠던 DTO 변환 방식과 이번 포스팅의 DTO 직접 조회 방식의 차이를 알아보자. 

 

 

a) DTO 직접 조회 방식의 장단점 

- DTO 직접 조회 방식은 데이터를 선택적으로 조회할 수 있다는 것이 장점이자 단점이다.

- 데이터가 선택적 조회되므로 DB 네트워크의 비용이 저렴하다는 것이 장점이다. 

- 반환형을 DTO로 선언하므로 해당 DTO에 한정적으로 사용된다는 것이 단점이다.

- 즉, 조회된 데이터의 재사용성이 좋지 않다. 

 

 

b) DTO 변환 방식의 장단점 

- 객체가 조회 대상이므로 필요하지 않은 데이터까지 모두 조회된다는 것이 장점이자 단점이다.

- 객체의 모든 데이터가 조회되므로 상대적으로 DB 네트워크 비용이 비싸다는 것이 단점이다.

- 객체의 모든 데이터가 조회되므로 다양한 DTO에서 조회 결과를 활용할 수 있다는 장점이 있다.

- 즉, 조회된 데이터의 재사용성이 좋다.  

 

 

c) 결론

- DTO 변환 방식과 DTO 직접 조회 방식은 서로 상반된 특성을 가진다.

- 그러므로 특정 방식이 우위에 있다고 말할 수 없다. 

 

- 사실 조회 방식에 따른 DB 네트워크 비용을 비교하는 것은 요즘 서버 스펙상 무의미하다. 

- 즉, 데이터의 재사용성 또는 트래픽 수요에 따라 두 방식 중 하나를 사용하면 되겠다. 

 

 

3. 권장하는 조회 방식

- 지금까지 DTO 변환 방식와 DTO 직접 조회 방식에 대해서 알아보았다.

- 이 두가지 방식은 상반된 특성을 가지고 있으므로 어떤 방식이 더 우위에 있다고 설명하기 어렵다.

- 다만, 유지보수성과 재사용성을 고려했을 때 DTO 변환 방식을 우선적으로 사용하는 것을 권장한다.

 

- 대부분의 쿼리 성능 이슈는 Fetch Join을 사용하면 해결된다. 

- 그러므로 우선 DTO 변환 방식을 우선적으로 사용한다.

 

- Fetch Join을 사용해도 쿼리 성능 이슈가 해결되지 않는다면 DTO 직접 조회 방식을 적용한다.

- DTO 직접 조회 방식을 사용해도 문제가 해결되지 않는다면 네이티브 SQL을 사용하여 해결하는 것을 권장한다.  

댓글