JPA를 사용한 API 개발에서 성능 최적화와 효율적인 데이터 처리는 실무의 핵심입니다. 이번 글에서는 실무에서 꼭 알아야 할 JPA의 최적화 기술을 총망라하여 소개합니다. DTO 변환, Fetch Join, N+1 문제 해결, 배치 페칭 등 필수적인 주제들을 구체적 사례와 함께 다룹니다.
@JsonIgnore 같은 설정이 필요합니다.Lazy Loading)으로 인해 추가 쿼리가 발생하는 문제입니다.Order 10건을 조회하면, 각 Order의 Member와 OrderItem을 조회하기 위해 10+N번의 쿼리가 실행됩니다.Fetch Join:
@Query("select o from Order o join fetch o.member join fetch o.delivery")
List<Order> findAllWithMemberDelivery();
배치 페칭:
@BatchSize 또는 hibernate.default_batch_fetch_size 설정으로 최적화.@BatchSize(size = 100)
private List<OrderItem> orderItems;
@Query("select new com.example.OrderDto(o.id, m.name, d.address) from Order o join o.member m join o.delivery d")
List<OrderDto> findOrderDtos();
public List<Order> findAllWithMemberDelivery(int offset, int limit) {
return em.createQuery("select o from Order o join fetch o.member join fetch o.delivery", Order.class)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public List<OrderQueryDto> findAllByDto() {
List<OrderQueryDto> result = findOrders();
result.forEach(o -> {
List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId());
o.setOrderItems(orderItems);
});
return result;
}
public List<OrderQueryDto> findAllByDto_optimization() {
List<OrderQueryDto> result = findOrders();
Map<Long, List<OrderItemQueryDto>> orderItemMap = findOrderItemMap(toOrderIds(result));
result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));
return result;
}
public List<OrderFlatDto> findAllByDto_flat() {
return em.createQuery(
"select new jpabook.jpashop.repository.order.query.OrderFlatDto(" +
"o.id, m.name, o.orderDate, o.status, d.address, " +
"i.name, oi.orderPrice, oi.count) " +
"from Order o join o.member m join o.delivery d join o.orderItems oi join oi.item i",
OrderFlatDto.class)
.getResultList();
}
| 방식 | 특징 | 장점 | 단점 |
|---|---|---|---|
| V3.1 | 엔티티 조회 + 페이징 가능 | N+1 문제 해결, 페이징 가능 | Fetch Join 남용 시 성능 저하 가능 |
| V4 | DTO 조회 + 추가 쿼리 실행 | DTO 활용으로 명확한 데이터 정의 | N+1 문제 발생 가능 |
| V5 | DTO 조회 + 컬렉션 최적화 | 쿼리 호출 수 감소, N+1 문제 해결 | 쿼리 작성 복잡 |
| V6 | 플랫 데이터 + 애플리케이션 처리 | 쿼리 1번 실행 | 중복 데이터 전송, 페이징 불가 |
JPA 최적화를 이해하고 실무에 적용하여, 고성능 API를 구축해 보세요!