JPA에서 연관 관계가 LAZY 로딩일 때,
1번의 메인 쿼리 실행 후, 연관된 엔티티를 N번 추가 조회하는 현상
java
복사편집
List<Post> posts = postRepository.findAll();
for (Post post : posts) {
System.out.println(post.getUser().getName()); // 작성자 정보 조회
}
Post와 User는 @ManyToOne(fetch = LAZY) 관계Post 조회 쿼리: 1번User 조회 쿼리: 10번 → 총 11번 쿼리 발생 = N+1 문제| 방식 | 설명 | 비유 |
|---|---|---|
| ❌ N+1 문제 | 게시글은 한 번에 가져오고, 작성자는 하나씩 가져옴 | 라면은 상자째 받지만, 브랜드 정보는 본사에 하나씩 전화해서 확인 |
| ✅ fetch join / EntityGraph | 게시글과 작성자를 한 번에 가져옴 | 라면 10개와 브랜드 정보가 한 포장에 같이 들어 있음 |
java
복사편집
@Query("SELECT p FROM Post p JOIN FETCH p.user")
List<Post> findAllWithUser();
JOIN FETCH 명시Post와 User를 한 번에 JOIN해서 가져옴java
복사편집
@EntityGraph(attributePaths = {"user"})
List<Post> findAll();
@EntityGraph 선언JOIN FETCH처럼 작동| 방식 | 쿼리 횟수 | 설명 |
|---|---|---|
| ❌ 일반 LAZY | 1 + N | Post 1번 + User N번 |
| ✅ Fetch Join | 1 | Post + User JOIN |
| ✅ EntityGraph | 1 | Post + User JOIN (선언형) |
| 항목 | Fetch Join | EntityGraph |
|---|---|---|
| 사용 위치 | JPQL 쿼리 안 | 메서드 위 어노테이션 |
| 조건 추가 | O (WHERE, ORDER 가능) | X (정적 fetch만 가능) |
| 복잡한 JOIN | O (정밀 제어 가능) | X (과도한 JOIN 가능성) |
| 간단한 fetch | 덜 직관적 | ✅ 가장 깔끔 |
N+1 문제 = "1번 가져오고, 연관된 것 N번 더 가져오는 비효율"
이를 해결하는 방법은:
- 복잡한 조건이 있으면 →
fetch join- 단순 조회라면 →
EntityGraph