Lazy Loading과 N+1
연관 데이터를 늦게 읽는 전략이 반복 Query를 만드는 조건을 설명한다.
이 문서의 목차
Overview
목록 Query 한 번 뒤에 각 항목의 연관관계 Query가 반복되는 현상이다.
핵심 원리
Lazy Loading 자체가 문제가 아니라 Use Case가 필요한 데이터 범위를 Query가 표현하지 못할 때 문제가 된다.
flowchart LR; L[List Query 1] --> A[Item 1 Query]; L --> B[Item 2 Query]; L --> N[Item N Query]
실무에서 발생하는 문제
목록 조회 후 각 Entity의 지연 연관을 순회하면 Query가 N번 추가된다. 개발 데이터가 적거나 Transaction 안에서만 동작해 Review에서 놓치기 쉽다.
Trade-off
Fetch Join과 EntityGraph는 왕복을 줄이지만 Collection 중복과 Pagination 제약이 있다. Batch Fetch는 Query 수를 줄이되 필요한 Row보다 많이 읽을 수 있다.
흔한 오해
LAZY 설정 자체가 N+1 원인은 아니다. 접근 시점과 조회 계획이 문제이며 모든 연관을 EAGER로 바꾸면 더 큰 Query와 예측 불가능한 조회를 만든다.
Interview Questions
- 이 기술이 해결하는 핵심 문제는 무엇인가?
- 내부에서는 어떤 순서로 동작하는가?
- 운영 환경에서 어떤 지표와 실패 모드를 확인해야 하는가?
왜 필요한가
ORM의 객체 탐색과 실제 SQL 실행 시점을 연결해 Query 폭증을 예방한다. API 응답을 만들 때 어떤 연관 데이터를 한 번에 필요로 하는지 Use Case별 Fetch Plan을 정한다.
내부 동작
LAZY 연관은 Proxy 또는 Persistent Collection으로 보관되며 실제 접근 시 영속성 Context를 통해 SQL을 실행한다. 부모 N개를 조회한 뒤 각 연관을 접근하면 최초 1회와 추가 N회 Query가 생긴다.
Example
주문 100건과 각 회원 이름이 필요하면 To-one Fetch Join 또는 EntityGraph를 검토한다. Collection과 Pagination이 함께 필요하면 먼저 ID Page를 조회한 뒤 IN 조회나 Batch Fetch로 분리한다.
Production Considerations
Integration Test에서 SQL 수를 검증하고 APM의 요청당 DB 호출 수와 rows examined를 본다. OSIV를 끈 환경에서 필요한 데이터를 Service Transaction 안에서 DTO로 완성하도록 경계를 명확히 한다.
Related Topics
Fetch Join, 영속성 Context, OSIV와 함께 Fetch Plan을 설계한다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.