엔티티 조회해서 그대로 반환하는 v1 방식은 큰일난다 ->안돼!
엔티티 스펙 변하면 api 스펙 자체가 변하기 때문에 사용하면 안된다.
엔티티 조회한 후에는 dto 변환해서 api스펙 딱 맞게 반환하는 것을 권장한다. 엔티티 조회 후 DTO로 변환은 적절한 위치에서 하면된다.
이건 여러 테이블을 조인하게 되면 성능이 안나온다.
이때는 fetch 조인을 사용하면 쿼리 수를 최적화 가능
문제는 fetch 조인의 한계 : 컬렉션의 경우, 페이징을 못한다.
실무에선 페이징 쓸 일이 많다. 적절한 페이지 단위로 끊어서 데이터 넘길일이 많음. 컬렉션은 페치 조인시 페이징이 불가능
ToOne(ManyToOne, OneToOne) 페치 조인으로 쿼리 수를 최적화 하고,
컬렉션 (이전 예제에서 OrderItem) 의 경우 페치 조인대신에(페이징을 위해서) 지연 로딩유지를 하고 그 다음 대신에 hibernate.default_batch_fetch_size 옵션을 주거나 세부적으로 하려면 @BatchSize 로 최적화(미리 당겨와서 한번에 조회 가능하도록)
JPA에서 DTO를 직접 조회하는 예제는 V4
sql 직접 짤때와 마찬가지로 컬렉션은 일대 다 관계가 된다. (예를 들어서 order하나에 orderItem이 2개인 경우를 생각)
원하는 게 order기준으로 처리하고 페이징 처리하고 싶은거라면
orderItem인 컬렉션 쪽을 어떻게 하냐
in절을 사용해서 메모리에 미리 로딩해서 그 다음에 order랑 루프를 돌면서 맞추기 -> v5버전
Flat데이터 최적화 : JOIN결과를 그대로 조회 후 애플리케이션에서 다 이거를 발라낸다 (원하는 모양으로 직접 변환) -> v6버전
그러면 어떤 순서로 하면 좋은가 ?
엔티티로 조회할지 dto로 조회할지 처음엔 고민이 된다.
🔶김영한 스앵님이 권장하는 방법은 🔶
엔티티 조회 방식으로 우선 접근
그리고 페치조인으로 쿼리 수 최적화 하고,
컬렉션 있다면
- 페이징 필요하면 -> hibernate.default_batch_fetch_size , @BatchSize 옵션 써서 최적화 (이 옵션은 default로 넣으면된다)
- 페이징 필요없다면 페치 조인 사용
그런데 만일 이 방식이 안된다면
DTO 조회 방식을 쓰고
DTO 조회 방식이 안된다면
NativeSQL or 스프링 JdbcTemplate
쿼리가 한번 시행된다고 v6가 좋은건 아니다 (뜨끔 🙄)
많은 코드를 직접 많이 작성하고 코드를 많이 변경해야함.
V4는 코드가 단순하고(개발자가 이해하기 쉬움, 직관적이라 금방 이해된다. 문맥상 특정 주문 한건만 단건 조회하게 된다면 이 방식으로 해도 성능 잘 나옴) (데이터 한번만 조회하면 되는데 v5로 괜히 넘어가서 섣부른 최적화가 될 가능성 있음)
v5는 코드가 복잡함. in 절을 이용해서 v4의 n+1에서 1+1로 성능이 좋아진다. 여러 건 조회의 경우 v5가 성능 훨씬 좋음 (한번에 가져온 다음에 메모리에서 재조립) 페이징 하는 상황이고, 데이터를 여러개 전송해야하는 경우엔 v5써야함
v6는 완전히 다른 접근방식. 쿼리조차도 한 번으로 해결. 데이터 다 긁어도 (1대 다가 되더라도, 중복되더라도) (orderItems 수만큼 뻥튀기된 중복 데이터가 네트워크로 전송됨, 그러나 쿼리는 1번) 네트워크 호출 회수는 1번
대신 Order를 기준으로 페이징이 불가능. OrderItems으로는 페이징 가능. 페이징 들어가는 경우엔 v6쓰기 애매함.데이터 중복 전송되기 때문에 네트워크 전송량 때문에 v5보다 성능 안나올 수도 있음
이런 tradeoff를 생각하고 써야한다.