1. N+1 문제란 무엇인가?
N+1 문제란 연관 관계가 설정된 엔티티를 조회할 때, 의도하지 않은 추가 쿼리가 데이터의 개수(N)만큼 더 실행되는 현상을 말한다.
- 1: 모든 데이터를 가져오기 위한 초기 쿼리 1회
- N: 조회된 각 데이터와 연관된 엔티티를 가져오기 위한 추가 쿼리 N회
결과적으로 단 한 번의 조회로 끝날 줄 알았던 작업이 N+1번의 쿼리로 불어나 서버와 DB에 과부하를 주게 된다.
2. 왜 발생하나요? (원리)
JPA는 기본적으로 지연 로딩(Lazy Loading) 방식을 권장한다.
- 처음 엔티티를 조회할 때는 연관된 엔티티를 실제 데이터가 아닌 프록시(Proxy) 객체로 채워둔다.
- 이후 루프를 돌며 연관 객체에 접근(예: member.getTeam().getName())하는 순간, JPA는 비어있는 프록시를 채우기 위해 DB에 추가 쿼리를 날린다.
- 만약 멤버가 100명이라면, 팀 이름을 확인할 때마다 100번의 추가 쿼리가 발생하는 것이다.
3. 해결 방법 (Solution)
가장 대표적인 3가지 해결 방법을 정리.
페치 조인 (Fetch Join) - 가장 대중적인 방법
JPQL에서 join fetch 키워드를 사용하여 연관된 엔티티를 한 번의 쿼리로 함께 가져오는 방식입니다.
- 장점: 한 번의 쿼리로 해결되므로 N+1 문제가 완벽히 사라집니다.
- 단점: 페이징 처리 시 모든 데이터를 메모리에 올린 뒤 계산하므로 OutOfMemoryError 위험이 있습니다.
@EntityGraph
JPA 표준 어노테이션을 사용하여 어떤 연관 관계를 함께 조회할지 명시하는 방법입니다.
- 장점: JPQL 쿼리 없이도 설정 가능하여 가독성이 좋습니다.
- 단점: 쿼리가 복잡해지면 페치 조인보다 유연성이 떨어집니다.
Batch Size 설정 (Global/Local)
연관된 엔티티를 조회할 때 하나씩 가져오는 게 아니라, 설정한 크기만큼 IN 절을 사용하여 묶어서 가져오는 방식입니다.
- 장점: N+1 문제를 완전히 없애지는 못하지만, 1 + (N / BatchSize)로 쿼리 횟수를 획기적으로 줄여줍니다. 페이징 문제도 해결 가능합니다.
- 설정: spring.jpa.properties.hibernate.default_batch_fetch_size: 100
면접 답변식 요약
N+1 문제는 연관 관계가 설정된 엔티티를 조회할 때, 의도한 1번의 쿼리 외에 연관 엔티티 조회를 위한 추가 쿼리가 데이터 개수 N만큼 발생하는 성능 문제입니다. 이는 주로 지연 로딩을 사용하는 객체 그래프 탐색 과정에서 발생합니다.
이를 해결하기 위해 가장 많이 사용하는 방법은 페치 조인(Fetch Join)입니다. 한 번의 쿼리로 연관 객체까지 미리 가져와 성능을 최적화할 수 있습니다. 다만 페이징 처리가 필요한 경우에는 메모리 부하 위험이 있어, 대신 Batch Size 설정을 통해 쿼리를 묶어 보내거나 필요한 경우에만 EntityGraph를 활용하여 유연하게 대응합니다.
'Daily Dev Q&A 정리 템플릿' 카테고리의 다른 글
| 26.01.16 inner조인과 outter조인이란? (0) | 2026.01.16 |
|---|---|
| 26.01.14 DNS란? (0) | 2026.01.14 |
| 26.01.09 JPA와 MyBatis를 비교해서 설명해주세요. (0) | 2026.01.09 |
| 26.01.08 JPA의 영속성 컨텍스트란? (0) | 2026.01.08 |
| 26.01.07 ORM이란? (0) | 2026.01.07 |