한 요청 안에서 생기는 쿼리 증식
목록은 한 번 읽지만, 각 행의 연관 엔티티 접근이 다시 DB를 왕복해 총 쿼리가 1 + N으로 늘어납니다.
핵심은 같은 요청 안에서 조회가 끝나지 않는다는 점입니다. 데이터가 늘수록 추가 왕복도 같은 비율로 늘어나 운영에서 갑자기 병목이 됩니다.
1 + N
목록 1번 + 연관 조회 N번
실제로 일어나는 구조
공유 시작점은 한 번이지만, 각 게시글이 작성자를 읽는 순간 추가 조회가 행마다 붙습니다.
게시글 목록 5건을 먼저 조회
애플리케이션은 게시글 행만 받았고, 작성자 정보는 아직 비어 있습니다.
첫 쿼리 1번
SELECT * FROM posts LIMIT 5;
각 행에서 post.author 접근
게시글 #101
author_id = 7
→
SELECT * FROM users WHERE id = 7;
게시글 #102
author_id = 12
→
SELECT * FROM users WHERE id = 12;
게시글 #103
author_id = 19
→
SELECT * FROM users WHERE id = 19;
이 패턴이 게시글 수만큼 반복
왜 운영에서 치명적인가
문제는 "조금 더 느림"이 아니라, 데이터가 쌓일수록 쿼리 수가 그대로 선형 증가한다는 점입니다.
한 번의 목록 조회가 끝나지 않습니다.
연관 엔티티를 묶어서 가져오지 않으면, 행을 순회하는 코드가 DB 왕복을 계속 만들어 냅니다.
테스트 데이터 5건
게시글 1번 + 작성자 5번
6번
운영 데이터 100000건
게시글 1번 + 작성자 100000번
100001번
1
DB 연결 점유 증가
같은 요청 하나가 더 오래 커넥션을 붙잡습니다.
2
응답 시간 급증
행 수 증가가 곧 추가 왕복 증가로 이어집니다.
3
테스트에서는 숨기 쉬움
적은 데이터에서는 6번 쿼리가 티 나지 않아 운영에서 늦게 드러납니다.
학습 포인트: N+1 문제의 본질은 "한 쿼리가 느리다"가 아니라, 공유 시작점 하나 뒤에 같은 연관 조회가 행마다 다시 실행되는 구조입니다.