🎯 N+1 문제란?
N+1 문제는 연관 관계가 설정된 엔티티를 조회할 때, 추가적인 쿼리가 반복적으로 발생하는 현상
List<Post> posts = postRepository.findAll(); // 1번 쿼리
for (Post post : posts) {
System.out.println(post.getUser().getNickname()); // N번 추가 쿼리 발생
}
위 코드에서 getUser() 호출 시마다 쿼리가 발생해서 결과적으로 1 + N번의 쿼리가 발생
🤔 발생 이유
JPA에서는 연관 관계를 @ManyToOne, @OneToMany 등으로 설정할 수 있고, 이때 기본적으로 LAZY 로딩 전략을 사용
처음에는 Post만 조회하고, getUser() 등 연관 엔티티 접근 시 그때서야 쿼리를 날림
⚠️ N+1 발생 조건
| 조건 | 설명 |
| 연관 엔티티가 Lazy 로딩 상태 | 기본 설정 |
| 서비스 단에서 .getUser(), .getComments() 등 호출 | 추가 쿼리 발생 |
| 반복문 등에서 연관 접근 | N개 쿼리 발 |

🕵️♂️ 확인 방법
- Hibernate SQL 로그 활성화
- 반복문 안에서 연관 객체 접근
- 콘솔에 쿼리 수 확인
spring:
jpa:
properties:
hibernate:
use_sql_comments: true
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql: TRACE
✅ 해결 방법
1️⃣ DTO Projection 방식 (QueryDSL)
엔티티가 아닌 DTO로 직접 필요한 필드만 조회
queryFactory
.select(Projections.constructor(PostDto.class,
post.id,
post.title,
user.nickname
))
.from(post)
.join(post.user, user)
.fetch();
- 연관 객체를 .join()으로 명시
- 쿼리 1번에 필요한 데이터 모두 조회
- N+1 완전 차단
2️⃣ Fetch Join 사용 (JPQL)
엔티티를 직접 조회해야 할 경우 사용 가능
@Query("SELECT p FROM Post p JOIN FETCH p.user")
List<Post> findAllWithUser();
- 연관 객체도 함께 가져옴 (1번 쿼리)
- 단, 컬렉션(@OneToMany)과 함께 쓰면 페이징 불가
3️⃣ @EntityGraph 사용
JPA 기능만으로 Lazy 로딩을 미리 fetch 하도록 지시
@EntityGraph(attributePaths = {"user"})
@Query("SELECT p FROM Post p")
List<Post> findAllWithUserGraph();
- 선언형으로 간단
- 기본 쿼리에 fetch join 붙는 효과
- 복잡한 join 구조에는 한계
'SPRING' 카테고리의 다른 글
| [Spring] 스프링 테스트 코드 (3) | 2025.04.10 |
|---|---|
| [Spring] 인덱싱 (0) | 2025.04.09 |
| [SPRING] Spring Security + JWT 인증 예외 처리 흐름 (1) | 2025.04.01 |
| [SPRING] JWT (2) | 2025.04.01 |
| [SPRING] JPAQueryFactory (0) | 2025.03.30 |