728x90
1.N+1 문제
하나의 쿼리(1)로 엔티티 리스트를 조회한 후,
연관된 엔티티들을 조회할 때 각각의 건마다 추가 쿼리(N개)가 발생하는 현상
만약 다음 처럼 회원과 주문 엔티티가 있을때
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "member")
private List<Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
private String itemName;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
}
다음은 만약 주문이 10개라고 한다면
모든 주문을 가져오는 쿼리 1번 + 각 주문마다 Member를 조회하는 쿼리 N번 총 쿼리 N+1 번이 일어나게 된다.
이는 크게 볼때 성능저하를 불러올수있다.
List<Order> orders = orderRepository.findAll(); // 주문 전체 조회
for (Order order : orders) {
System.out.println(order.getMember().getName()); // 각 주문의 회원 이름 출력
}
1-1. 지연로딩(LAZY Loading)
연관된 엔티티를 필요할 때 로딩하는 방식
연관된 자식 엔티티는 조회하지 않고, 해당 자식 엔티티에 접근할 때 쿼리가 실행된다.
필요할 때만 연관된 엔티티를 조회하므로 데이터베이스 쿼리가 더 적게 발생하지만 연관된 엔티티에 접근할 때마다 쿼리가 추가로 실행되며 N+1 문제를 야기함
@ManyToOne(fetch = FetchType.LAZY)
1-2. 즉시로딩(EAGER Loading)
연관된 엔티티를 조회할 때, 해당 엔티티를 즉시 로딩하는 방식
연관된 엔티티가 즉시 로딩되므로, 연관된 엔티티를 쿼리할 때 추가적인 쿼리가 실행되지 않지만 불필요한 데이터까지 로딩하게 되어 성능 저하가 일어날수 있음
@ManyToOne(fetch = FetchType.EAGER)
2.해결법
2-1.Fetch Join 사용
JOIN FETCH를 사용하여 연관된 개체를 함께 조회한다.
연관된 데이터를 즉시 로딩하는 방법이다. (지연로딩을 즉시로딩으로 바꿈)
(연관된 데이터를 참조할 때 추가 쿼리가 실행안되게함)
@Query("SELECT o FROM Order o JOIN FETCH o.member")
List<Order> findAllWithMember();
이제 아까 N+1 문제가 발생하던 코드와 달리 1번 쿼리를 조회한뒤
order.getMember() 부분에서 추가 쿼리가 발생하지 않게 된다.
List<Order> orders = orderRepository.findAllWithMember(); // Order와 Member를 한 번에 조회
for (Order order : orders) {
System.out.println(order.getMember().getName()); // getMember() 호출 시 추가 쿼리 없음
}
2-2.@EntityGraph
특정 연관 엔티티를 명시적으로 한 번에 로딩하도록 제어하는 방법
지연 로딩을 사용하면서도 필요한 연관 엔티티를 한 번의 쿼리로 함께 로드할 수 있게 해줌
(연관된 엔티티만 즉시 로딩)
attributePaths = "member"를 통해 Order와 연관된 Member를 한 번의 쿼리로 조회할 수 있다.
@EntityGraph(attributePaths = "member")
List<Order> findAll();
728x90
'BackEnd > SpringBoot' 카테고리의 다른 글
[SpringBoot] MySQL springBoot 연결하기 (0) | 2025.05.14 |
---|---|
[SpringBoot] Maven -> Gradle 마이그레이션하기 (0) | 2025.05.11 |
[SpringBoot] Spring MVC Request Lifecycle (0) | 2025.05.07 |