본문 바로가기
Q&A해결2026년 7월 28일1분 읽기

Prisma에서 목록 하나 뿌리는데 쿼리가 수십 개씩 날아가요 (N+1)

o
orm_jiwoo
조회 895댓글 1

게시글 목록 20개에 작성자 정보랑 댓글 개수를 같이 뿌리는데, Prisma 쿼리 로그를 켜보니 요청 한 번에 SELECT가 40개 넘게 나갑니다. 리스트 하나 그리는데 이게 맞나 싶어서요.

코드는 이렇게 짰어요.

const posts = await prisma.post.findMany({ take: 20 })
for (const p of posts) {
  p.author = await prisma.user.findUnique({ where: { id: p.authorId } })
  p.commentCount = await prisma.comment.count({ where: { postId: p.id } })
}

동작은 하는데 목록이 길어지면 눈에 띄게 느려요. 반복문 안에서 쿼리를 도는 게 문제인 건 어렴풋이 아는데, Prisma에서는 이걸 어떻게 한 번에 가져오나요?

댓글 1

김김영삼3개월 전
정확히 N+1이에요. findMany 1번 + 작성자 20번 + 댓글수 20번 = 41번 나가는 거죠. 반복문 안에서 쿼리를 도는 순간 목록 길이에 비례해서 쿼리가 늘어납니다.

Prisma는 관계를 한 번에 끌어오는 include/select가 있으니 반복문 자체를 없애면 됩니다. 이렇게요.

const posts = await prisma.post.findMany({
take: 20,
include: {
author: true,
_count: { select: { comments: true } },
},
})

이러면 author는 관계 로딩으로 묶여서 처리되고, 댓글 개수는 _count로 받아옵니다. 각 post의 _count.comments 에 숫자가 들어와요. count를 따로 20번 칠 필요가 없어지는 거죠. 반복문에서 findUnique/count 돌리던 걸 통째로 지우시면 됩니다.

작성자 정보 전부가 필요 없으면 author: true 대신 author: { select: { id: true, name: true } } 로 필요한 컬럼만 집으세요. 목록 쿼리에서 안 쓰는 컬럼까지 다 긁는 것도 은근히 비용입니다.

참고로 최신 Prisma는 relationLoadStrategy: 'join' 옵션이 있어서 관계를 SQL JOIN 한 방으로 가져오게 할 수도 있어요. 기본값은 쿼리를 몇 개로 나눠서 앱에서 합치는 방식인데, 이것도 반복문 N+1과는 차원이 다르게(관계당 한 번) 처리되니 우선은 include로 바꾸는 것만으로도 41개가 두어 개로 줄어듭니다.
Ctrl+Enter로 등록