카테고리 없음

[스프링부트] 실전! 스프링 부트와 JPA 활용2 컬렉션 조회 최적화 #7 API 개발 고급 정리

aSpring 2023. 11. 26. 17:38
728x90
728x90
※ 본 포스팅은 김영한 강사님의 인프런 '실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화' 강의를 들으며 작성한 수강일지 입니다.

 
 

 

 

| API 개발 고급 - 컬렉션 조회 최적화

1. 주문 조회 V1: 엔티티 직접 노출
2. 주문 조회 V2: 엔티티를 DTO로 변환
3. 주문 조회 V3: 엔티티를 DTO로 변환 - 페치 조인 최적화
4. 주문 조회 V3.1: 엔티티를 DTO로 변환 - 페이징과 한계 돌파
5. 주문 조회 V4: JPA에서 DTO 직접 조회
6. 주문 조회 V5: JPA에서 DTO 직접 조회 - 컬렉션 조회 최적화 
7. 주문 조회 V6: JPA에서 DTO로 직접 조회, 플랫 데이터 최적화
8. API 개발 고급 정리

API 개발 고급 정리

정리

  • 엔티티 조회
    • 엔티티를 조회해서 그대로 반환: V1 -> API 스펙 자체가 변하기 때문에 이렇게 하면 안됨
    • 엔티티 조회 후 DTO로 변환: V2 -> 컨트롤러에서 DTO로 변환하기 // V1,2 -> 여러 테이블 조인 시 성능이 나오지 않음
    • 페치 조인으로 쿼리 수 최적화: V3 -> 쿼리 수 최적화 가능, but 컬렉션의 경우 fetch join 시 페이징을 못함
    • 컬렉션 페이징과 한계 돌파: V3.1
      • 컬렉션은 페치 조인시 페이징이 불가능
      • ToOne 관계(컬렉션이 아닌 것)는 페치 조인으로 쿼리 수 최적화
      • 컬렉션은 페치 조인 대신에 지연 로딩을 유지하고, hibernate.default_batch_fetch_size, @BatchSize로 최적화
        • order 입장에서는 orderItems가 해당됨
  • DTO 직접 조회
    • JPA에서 DTO를 직접 조회: V4
    • 컬렉션 조회 최적화 - 일대다 관계인 컬렉션은 IN 절을 활용해서 메모리에 미리 조회해서 최적화: V5
      • order와 loop 돌리면서...
    • 플랫 데이터 최적화 - JOIN 결과를 그대로 조회 후 애플리케이션에서 원하는 모양으로 직접 변환: V6
      • 전부 메모리에 올려서...

 

강사님 권장 순서

  1. 엔티티 조회 방식으로 우선 접근
    1. 페치조인으로 쿼리 수를 최적화
    2. 컬렉션 최적화
      1. 페이징 필요O -> hibernate.default_batch_fetch_size , @BatchSize로 최적화
      2. 페이징 필요X -> 페치 조인 사용
  2. 엔티티 조회 방식으로 해결이 안되면 DTO 조회 방식 사용
  3. DTO 조회 방식으로 해결이 안되면 NativeSQL or 스프링 JdbcTemplate
참고: 엔티티 조회 방식은 페치 조인이나, hibernate.default_batch_fetch_size, @BatchSize 같이 코드를 거의 수정하지 않고, 옵션만 약간 변경해서, 다양한 성능 최적화를 시도할 수 있다. 반면에 DTO를 직접 조회하는 방식은 성능을 최적화 하거나 성능 최적화 방식을 변경할 때 많은 코드를 변경해야 한다.

 

절대 Entity를 캐시하면 안됨

- DTO로 캐시하기

 

참고: 개발자는 성능 최적화와 코드 복잡도 사이에서 줄타기를 해야 한다. 항상 그런 것은 아니지만, 보통 성능 최적화는 단순한 코드를 복잡한 코드로 몰고간다.
엔티티 조회 방식은 JPA가 많은 부분을 최적화 해주기 때문에, 단순한 코드를 유지하면서, 성능을 최적화 할 수 있다.
반면에 DTO 조회 방식은 SQL을 직접 다루는 것과 유사하기 때문에, 둘 사이에 줄타기를 해야 한다.

 

 

DTO 조회 방식의 선택지

  • DTO로 조회하는 방법도 각각 장단이 있다. V4, V5, V6에서 단순하게 쿼리가 1번 실행된다고 V6이 항상 좋은 방법인 것은 아니다.
  • V4는 코드가 단순하다(개발자가 이해하기 쉽고 명확하여 유지보수하기 좋음). 특정 주문 한건만 조회하면 이 방식을 사용해도 성능이 잘 나온다. 예를 들어서 조회 한 Order 데이터가 1건이면 OrderItem을 찾기 위한 쿼리도 1번만 실행하면 된다.
    • 단점 : loop 돌면서 orderItems를 계속 1번씩 찍어서 가지고 옴( N + 1 문제 )
  • V5코드가 복잡하다. 여러 주문을 한꺼번에 조회하는 경우에는 V4 대신에 이것을 최적화한 V5 방식을 사 용해야 한다. 예를 들어서 조회한 Order 데이터가 1000건인데, V4 방식을 그대로 사용하면, 쿼리가 총 1 + 1000번 실행된다. 여기서 1은 Order 를 조회한 쿼리고, 1000은 조회된 Order의 row 수다. V5 방식 으로 최적화 하면 쿼리가 총 1 + 1번만 실행된다. 상황에 따라 다르겠지만 운영 환경에서 100배 이상의 성능 차이가 날 수 있다.(IN 절로 한번에 가져와서 메모리에 올려놓고 그 이후에 돌리며 재조립. V4보다 성능은 확실히 나음) -> 쿼리 2번 호출 : 네트워크 2번 호출하지만 딱 정규화된 데이터를 가져 옴
  • V6는 완전히 다른 접근방식이다. 쿼리 한번으로 최적화 되어서 상당히 좋아보이지만, Order를 기준으로 페이징이 불가능하다(orderItem을 기준으로 페이징을 할 순 있음 -> 제일 많으므로). 실무에서는 이정도 데이터면 수백이나, 수천건 단위로 페이징 처리가 꼭 필요하므로, 이 경우 선택하기 어려운 방법이다. 그리고 데이터가 많으면 중복 전송이 증가해서 V5와 비교해서 성능 차이도 미비하다.(상황에 따라 V5가 더 나은 성능을 보일 수도 있음) -> 쿼리는 1번 호출
728x90
728x90