레이블이 Prisma인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Prisma인 게시물을 표시합니다. 모든 게시물 표시

수요일

Prisma N+1 문제 해결 — include·in 필터·dataloader로 쿼리 수 줄이기

🔍 검색 키워드: Prisma N+1 문제, Prisma include N+1 해결, Prisma findMany 반복문 쿼리, Prisma in 필터 authorId, Prisma dataloader findUnique 배치, Prisma 쿼리 수 줄이기

요약: Prisma의 N+1 문제는 쿼리 결과를 반복문으로 돌면서 결과마다 추가 쿼리를 한 번씩 실행할 때 생깁니다. Prisma 공식 문서는 include로 관계를 함께 읽거나, in 필터로 한 번에 조회하거나, relationLoadStrategy join 옵션을 쓰는 방법을 안내합니다. GraphQL 리졸버에서는 findUnique와 fluent API가 dataloader로 배치됩니다.

Prisma에서 N+1 문제란 무엇인가요?

Prisma 공식 문서(Query optimization and performance)는 N+1 문제를 "쿼리 결과를 반복하면서 결과마다 추가 쿼리를 하나씩 실행할 때 발생한다"는 취지로 정의합니다. 사용자 N명을 조회한 뒤 각 사용자의 게시글을 따로 조회하면 쿼리가 1 + N번 나갑니다. 데이터가 늘수록 쿼리 수가 함께 늘어 응답이 느려집니다.

어떤 코드에서 N+1이 생기나요?

반복문 안에서 findMany를 호출하는 경우

공식 문서의 나쁜 예시는 사용자를 조회한 뒤 forEach 안에서 사용자마다 게시글을 다시 조회하는 형태입니다. 아래는 같은 구조를 단순하게 옮긴 예시입니다.

const users = await prisma.user.findMany()

for (const user of users) {
  const posts = await prisma.post.findMany({
    where: { authorId: user.id },
  })
  // 사용자 수 N만큼 추가 쿼리 실행
}

GraphQL 리졸버에서 필드마다 쿼리하는 경우

공식 문서는 GraphQL이 필드마다 별도의 리졸버 함수를 실행한다고 설명합니다. 사용자 목록을 반환할 때 posts 리졸버가 사용자 수만큼 호출되고, 호출마다 쿼리를 실행하면 같은 N+1이 됩니다.

Prisma N+1은 어떻게 해결하나요?

공식 문서에 소개된 해결 방법은 아래 네 가지입니다. 어떤 방법이 맞는지는 쿼리 모양과 사용 중인 Prisma 버전에 따라 달라집니다.

방법 1: include로 관계를 함께 읽기

const users = await prisma.user.findMany({
  include: { posts: true },
})

공식 문서는 이 방식이 "SQL 쿼리를 두 개만 실행한다. 하나는 사용자를 가져오고 하나는 게시글을 가져온다"고 설명합니다. 반복문마다 쿼리하는 것보다 쿼리 수가 크게 줄어듭니다.

방법 2: in 필터로 한 번에 조회하기

const users = await prisma.user.findMany()
const userIds = users.map((u) => u.id)

const posts = await prisma.post.findMany({
  where: { authorId: { in: userIds } },
})

사용자를 먼저 조회하고, 모은 ID로 게시글을 한 번에 조회합니다. 공식 문서 기준으로 쿼리는 두 개입니다. 결과를 사용자별로 묶는 작업은 애플리케이션 코드에서 해야 합니다.

방법 3: relationLoadStrategy를 join으로 지정하기

const posts = await prisma.post.findMany({
  relationLoadStrategy: "join",
  include: { author: true },
})

공식 문서는 이 옵션을 쓰면 데이터베이스에 대해 쿼리가 하나만 실행된다고 설명합니다. 같은 페이지에 제한 사항도 적혀 있습니다. join 전략을 쓰려면 조회하는 모델의 where 조건이 스칼라 필드에 대한 equal 필터만으로 이루어져야 하고, 불리언 연산자나 관계 필터는 쓸 수 없습니다. 이 옵션이 어느 Prisma 버전부터 제공되는지는 해당 페이지에서 확인되지 않았으므로, 사용 중인 버전의 문서를 확인하세요.

방법 4: GraphQL에서 fluent API와 findUnique 사용하기

// posts 리졸버 안에서
return prisma.user
  .findUnique({ where: { id: parent.id } })
  .posts()

공식 문서는 Prisma의 dataloader가 같은 tick 안의 findUnique 쿼리를 자동으로 배치한다고 설명합니다. 또한 dataloader는 현재 findMany 쿼리를 배치하지 않으므로 GraphQL 리졸버에서는 이 우회 방법이 필요하다고 안내합니다. 문서에는 findMany 배치가 지원되거나 relationLoadStrategy가 join일 때는 이 우회가 필요 없어진다는 설명도 있습니다.

include가 쿼리를 몇 개 실행하는지 문서마다 다르게 적혀 있나요?

네. 이 글을 쓰면서 Prisma 공식 문서의 두 페이지를 확인했는데 설명이 달랐습니다. 쿼리 최적화 페이지는 include가 쿼리 두 개(사용자, 게시글)를 실행한다고 설명합니다. 반면 관계 쿼리(relation queries) 페이지는 include가 관계를 부모와 같은 쿼리에서 가져오며 상관 서브쿼리를 쓰는 SQL 한 개라고 설명합니다. 문서가 다루는 Prisma 버전이 달라서일 가능성이 있지만, 이 글에서 확인하지는 못했습니다. 실제 쿼리 수는 아래 방법으로 직접 확인하는 것이 가장 정확합니다.

쿼리가 몇 번 나가는지 어떻게 확인하나요?

PrismaClient 생성 시 로그 옵션을 켜면 실행되는 SQL을 볼 수 있습니다. 아래는 일반적으로 쓰는 설정 형태이며 이 글에서 직접 실행해 확인한 것은 아닙니다. 옵션 이름과 동작은 사용 중인 버전의 공식 문서를 참고하세요.

const prisma = new PrismaClient({
  log: ["query"],
})

반복문 하나를 실행했을 때 같은 모양의 쿼리가 N번 반복해서 찍힌다면 N+1입니다.

주의할 점

  • 쿼리 수가 적다고 항상 빠른 것은 아닙니다. 관계를 한 번에 읽으면 한 번에 가져오는 데이터 양이 커질 수 있습니다.
  • in 필터에 넣는 ID 목록이 매우 커지면 쿼리 자체가 무거워질 수 있습니다. 필요하면 나눠서 조회하세요.
  • join 전략에는 위에서 설명한 where 조건 제한이 있으므로 모든 쿼리에 적용할 수는 없습니다.
  • 이 글은 Prisma 공식 문서를 근거로 정리한 것이며 직접 실행해 쿼리 수를 측정한 결과가 아닙니다. 버전에 따라 동작이 다를 수 있으니 공식 문서와 쿼리 로그로 확인하세요.

정리표

방법공식 문서가 설명한 쿼리 수참고
반복문 안에서 조회1 + NN+1 문제 발생
include2 (쿼리 최적화 페이지 기준)관계 쿼리 페이지는 1로 설명, 버전 확인 필요
in 필터2결과 묶기는 코드에서 처리
relationLoadStrategy join1where 조건 제한 있음, 도입 버전은 문서 확인
fluent API + findUniquedataloader가 배치GraphQL 리졸버용, findMany는 배치 안 됨

자주 묻는 질문(FAQ)

Prisma에서 N+1 문제는 왜 생기나요?

결과 목록을 반복하면서 결과마다 쿼리를 따로 실행하기 때문입니다. 공식 문서도 반복문에서 결과당 추가 쿼리를 실행하는 경우로 정의합니다.

include를 쓰면 N+1이 해결되나요?

반복문마다 쿼리하는 것보다 쿼리 수가 크게 줄어듭니다. 다만 정확한 쿼리 수는 버전과 설정에 따라 다를 수 있으니 쿼리 로그로 확인하세요.

GraphQL에서 Prisma N+1은 어떻게 막나요?

공식 문서는 리졸버에서 findUnique와 fluent API를 쓰면 dataloader가 같은 tick의 쿼리를 배치한다고 안내합니다. findMany는 현재 배치되지 않는다고 적혀 있습니다.

relationLoadStrategy join은 항상 쓸 수 있나요?

아닙니다. where 조건이 스칼라 필드의 equal 필터만으로 구성되어야 하는 등 제한이 있습니다. 지원 여부는 사용 중인 Prisma 버전의 문서에서 확인하세요.

참고 문서: Prisma - Query optimization and performance · Prisma - Relation queries

최종 업데이트: 2026-11-07 · 작성: 내멋스레 사는 이야기