Topic (오늘의 주제)
JPA의 성능 킬러: N+1 문제와 해결책
Why (왜 사용하는가? 왜 중요한가?)
- 실무: JPA를 사용하는 프로젝트에서 앱 성능 저하의 원인 1순위입니다. 로컬(데이터 적음)에선 모르다가 운영(데이터 많음)에서 서버를 뻗게 만듭니다.
- 구조적 의미: ORM이 개발자 편의를 위해 제공하는 **지연 로딩(Lazy Loading)**이나 연관 관계 매핑이, 의도치 않게 과도한 쿼리를 유발하는 현상입니다.
- 면접 의도: JPA의 동작 원리를 이해하고 있는지, 그리고 이 치명적인 문제를 **어떤 기술(Fetch Join, Batch Size)**로 해결해본 경험이 있는지 검증합니다.
Core Concept (핵심 개념 정리)
| 요소 | 내용 |
| 개념 정의 | 1번의 쿼리로 가져온 결과(N개)를 사용할 때, 연관된 데이터를 얻기 위해 추가로 N번의 쿼리가 더 나가는 문제. (총 1 + N번 실행) |
| 발생 원인 | JPA가 객체를 조회할 때 연관된 객체(ManyToOne 등)를 바로 가져오지 않고, 실제 사용할 때 쿼리를 날리는(Lazy Loading) 특성 때문에 발생. (EAGER로 설정해도 최초 쿼리 후 즉시 N번 날아가므로 해결책이 아님) |
| 상황 예시 | Team(팀) 목록을 10개 조회함 (쿼리 1번). 각 팀의 Member(멤버) 이름을 출력하려고 for문을 돌림. 팀 1의 멤버 조회, 팀 2의 멤버 조회... (쿼리 10번 추가 발생). |
| 해결책 | 1. Fetch Join: 가장 확실한 방법. 조인을 통해 한 방 쿼리로 다 가져옴. 2. Batch Size: N번 나갈 쿼리를 IN 절로 묶어서 1/(Batch Size) 번으로 줄임. 3. EntityGraph: 어노테이션으로 Fetch Join 효과를 냄. |
Interview Answer Version (면접 답변식 요약)
"N+1 문제는 연관 관계가 설정된 엔티티를 조회할 때, 조회된 데이터 갯수(N)만큼 연관 데이터를 얻기 위한 추가 쿼리가 발생하는 현상입니다.
주로 지연 로딩(Lazy Loading) 상황에서 객체를 탐색할 때 발생하며, 이를 해결하기 위한 가장 일반적인 방법은 Fetch Join을 사용하여 SQL 한 번에 연관 데이터를 미리 가져오는 것입니다.
추가적으로 컬렉션 조회 시 페이징이 필요하다면 Batch Size 설정을 통해 쿼리 수를 최적화합니다."
Practical Tip (사용시 주의할 점 or 활용 예)
1. Fetch Join 사용법 (JPQL)
- 일반 join은 연관 엔티티를 SELECT 절에 포함하지 않지만, fetch join은 객체 그래프를 한 번에 긁어옵니다.
-
Java
// N+1 발생 코드 @Query("select t from Team t") List<Team> findAll(); // 해결 코드 (Fetch Join) @Query("select t from Team t join fetch t.members") List<Team> findAllJoinFetch();
2. Fetch Join과 페이징(Pagination) 주의
- 1:N 관계(컬렉션)를 Fetch Join 하면서 페이징(Pageable)을 하면, 하이버네이트는 "모든 데이터를 메모리에 퍼올린 다음" 페이징을 시도합니다. -> Out of Memory 발생 위험.
- 해결: 컬렉션 페이징이 필요하면 Fetch Join을 빼고, hibernate.default_batch_fetch_size 옵션(글로벌 설정)을 100~1000 정도로 설정하여 해결합니다. (IN 쿼리로 최적화됨)
예상 꼬리질문 정리
- Q: 일반 Join과 Fetch Join의 차이점은 무엇인가요?
- 답변: 일반 Join은 연관 엔티티를 쿼리 조건(WHERE)에는 쓸 수 있지만 SELECT 절에는 안 가져옵니다(영속화 안 됨). Fetch Join은 연관 엔티티까지 모두 조회해서 영속성 컨텍스트에 저장합니다.
- Q: Batch Size는 어떻게 동작하나요?
- 답변: 설정한 사이즈만큼 PK를 모아서 WHERE id IN (?, ?, ...) 형태의 쿼리를 날려, N번의 쿼리를 1번(혹은 소수)으로 줄여줍니다.
- Q: MultipleBagFetchException이 뭔가요?
- 답변: 2개 이상의 컬렉션을 동시에 Fetch Join 할 때 발생하는 에러입니다. 컬렉션 Fetch Join은 하나만 가능합니다.
'Archive > Daily Dev Q&A' 카테고리의 다른 글
| Daily Dev Q&A: ApplicationContext (0) | 2026.01.19 |
|---|---|
| Daily Dev Q&A: DNS (0) | 2026.01.14 |
| Daily Dev Q&A: Spring MVC (0) | 2026.01.13 |
| Daily Dev Q&A: JPA vs MyBatis (0) | 2026.01.09 |
| Daily Dev Q&A: 영속성 컨텍스트 (0) | 2026.01.09 |