📘 목차
1. N + 1 문제
N + 1 문제
- ORM 프레임워크(특히 JPA, Hibernate)를 사용할 때 가장 빈번하게 발생하는 성능 병목 현상이다.
- N + 1
- 1번의 쿼리로 기본 데이터(부모 엔티티)를 조회하고,
- 이후에 N번의 쿼리로 연관된 데이터(자식 엔티티)를 각각 조회하는 상황을 의미한다.
- 즉, 전체적으로는 N + 1번의 쿼리가 발생하여, 쿼리 수가 기하급수적으로 늘어나서 성능 저하를 유발한다.
N + 1 문제 발생 원리와 과정
- 부모 엔티티 한 번 조회 (1번 쿼리)
- 예: 게시글 10개를 조회한다.
- 쿼리 1번 실행 → select * from post
- 자식 엔티티를 지연로딩 방식으로 각각 조회 (N번 쿼리)
- 각 게시글마다 작성자(Member)를 접근하면, 지연로딩 때문에 작성자 정보를 별도 쿼리로 조회한다.
- 게시글이 10개면 작성자 조회 쿼리 10번 실행 → select * from member where member_id = ?
- 총 N + 1번 쿼리 실행
- 부모 조회 1번 + 자식 조회 N번 → 성능 저하 발생
왜 이런 일이 발생하는가?
- JPA는 기본적으로 연관된 엔티티를 지연로딩(LAZY)으로 설정한다.
- 즉, 실제로 참조 필드를 접근하는 시점에 DB에서 데이터를 가져온다.
- 하지만 이 접근이 여러 객체에 대해 반복되면, 매번 별도의 쿼리가 발생한다.
2. Fetch Join
Fetch Join은 JPA에서 N+1 문제를 해결하기 위해 사용하는 기법이다. 한 번의 쿼리로 연관된 엔티티를 함께 조회할 수 있다.
Entity Fetch Join
- 단일 연관관계(@ManyToOne, @OneToOne)에 주로 사용한다.
- 한 건의 부모 엔티티와 단일 자식 엔티티를 조인해서 함께 가져온다.
- 결과는 하나의 부모 엔티티이고, 조인된 자식 엔티티가 내부 필드로 채워진다.
select o from Order o join fetch o.member
- Order 1건 조회와 동시에 Order가 참조하는 Member를 함께 조회한다.
- 보통 ManyToOne, OneToOne 관계에서 사용하며, 즉시 로딩(eager loading) 효과가 있다.
Collection Fetch Join
- @OneToMany, @ManyToMany 같은 컬렉션 관계에 사용된다.
- 여러 개의 자식 엔티티(컬렉션)를 부모 엔티티와 함께 조회한다.
- 결과는 부모 엔티티가 컬렉션에 연관된 자식 엔티티들을 모두 포함한다.
- 주의점: 컬렉션 fetch join 시, 결과가 중복될 수 있어 페이징에 제한이 있다.
select o from Order o join fetch o.orderItems
- Order와 관련된 여러 OrderItem을 한 번에 조회한다.
3. @BatchSize 어노테이션
- Fetch Join 외에 N+1 문제를 완화하는 방법으로, Hibernate가 제공하는 기능이다.
- 연관된 엔티티를 지연로딩할 때, 한 번에 N개 단위로 묶어서 조회한다.
- 엔티티 클래스나 연관관계 필드에 적용 가능하다.
- ex, @BatchSize(size=10)을 설정하면 10개씩 묶어서 쿼리를 실행해 N+1 문제를 줄인다.
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY)
@BatchSize(size = 10)
private Member member;
}
- 위 예시에서는 여러 Order의 Member를 조회할 때 10개 단위로 묶어서 한 번에 쿼리 실행한다.
- 완전한 fetch join만큼 성능 최적화가 되지는 않지만, 쿼리 수를 크게 줄일 수 있다.
4. 정리
구분 | 설명 | 장점 | 주의사항 |
Entity Fetch Join | 단일 연관관계(@ManyToOne, @OneToOne)를 한 번에 조인해서 조회 | 1번 쿼리로 연관 엔티티 즉시 로딩 | 크게 문제 없음 |
Collection Fetch Join | 컬렉션 연관관계(@OneToMany, @ManyToMany)를 한 번에 조회 | 1번 쿼리로 컬렉션 포함 모든 데이터 조회 | 중복 데이터 발생, 페이징 불가 |
@BatchSize | 지연로딩 시 여러 개 묶어서 한 번에 쿼리 실행 (Hibernate 전용) | N+1 문제 완화, 쿼리 수 감소 | 완전한 fetch join보다 덜 효율적 |
👏 👏 👏 심화 Spring 강의 완강 👏 👏 👏
'Spring > 강의' 카테고리의 다른 글
[📕 JPA 심화] 2. 데이터베이스 다루기 (2) | 2025.06.26 |
---|---|
[📕 JPA 심화] 1. 프로젝트 세팅 (0) | 2025.06.26 |
[📘 심화 Spring] 3-3. JPQL (Java Persistence Query Language) (2) | 2025.06.09 |
[📘 심화 Spring] 3-2. API 예외 처리 (0) | 2025.06.09 |
[📘 심화 Spring] 3-1. Spring Bean 생명주기 (0) | 2025.06.09 |