
🔎 API 개발을 할 때 주의할 점을 정리한 글입니다.
📍엔티티를 노출시키지 말자
💡 컨트롤러에서 파라미터를 엔티티로 바로 받지 말고, 중간에 API 스펙에 맞는 DTO를 만들어서 활용하자.
💡 양방향 연관관계일 경우, 한쪽에 @JsonIgnore를 하지 않으면 무한루프에 빠질 수 있다.
📍연관관계 fetch를 LAZY로 설정하고, 필요할 때에만 페치 조인을 사용하자.
💡 물론 LAZY, EAGER 둘 다 N+1 문제를 야기시킬 수 있다.
💡 지연 로딩의 경우 , 조회한 order에 대해 member나 delivery를 필요로 하는 경우에 각 order별로 member나 delivery 조회 쿼리가 N번씩 나가게 된다.
💡 그러나 지연 로딩은 필요시에 DB에 바로 조회하지않고, 영속성 컨텍스트를 먼저 확인하기 때문에, 한번 조회했던 member에 대해서 쿼리가 하나 줄어들 수 있다.
💡 즉시 로딩의 경우, order를 조회후, 곧바로 연관관계에 있는 member와 delivery를 조회하기 위해 쿼리가 한꺼번에 나가므로, 이 때 예상치 못한 쿼리가 나가거나, 성능을 저하시킨다.
📍위의 N+1 문제를 해결하기 위해서는 어떻게 해야할까?
💡 페치 조인을 사용하자
em.createQuery(" select o from Order o " +
" join fetch o.member m " +
" join fetch o.delivery d ").getResultList();
연관관계 페치를 LAZY로 설정해도, 위처럼 페치 조인을 사용한다면 쿼리 한번에 모든 객체의 값을 채워서 가져올 수 있다.
📍쿼리 방식 선택 권장 순서
컬렉션을 페치조인할 경우 일대다 페치조인이 발생하여 데이터가 예측 할 수 없이 증가한다(N+1문제)
일대다에서는 일(1)을 기준으로 페이징을 해야하지만, 데이터는 다(Many)를 기준으로 row가 생성된다.
그래서 Order를 기준으로 페이징하고싶어도 다인 OrderItem이 기준이 되어버린다.
위 문제들을 해결하기 위해서, 우선 ToOne(OneToOne,ManyToOne) 관계를 모두 페치조인한다.(row수를 증가시키지 않는다.)
setMaxResult(limit), setFirstResult(offset)을 사용한다.
jpa.properties.hibernate.default_batch_fetch_size:
설정한 수 만큼 미리 데이터를 조회해둔다. 내가 설정한 수가 10, 총 데이터가 100일 경우
in 쿼리가 10번 나간다.
지연 로딩으로 발생해야 할 쿼리를 IN으로 한번에 모아 보내는 기능
-> ToOne 관계는 페이징시 row수를 증가시키지 않으므로 페치 조인하고, 컬렉션은 ToMany 관계이므로 default_batch_fetch_size를 사용한다.(maximum : 1000)