Prefetch 객체와 Subquery로 복잡한 N+1 문제 해결하기
Django ORM에서 기본 prefetch_related로 해결되지 않는 복잡한 N+1 문제를 Prefetch 객체와 Subquery를 활용해 효율적으로 해결하는 방법
기본 N+1 문제와 한계
# ❌ N+1 문제 발생 예시
categories = Category.objects.all()
for category in categories:
public_posts = category.posts.filter(is_public=True) # 각 루프마다 쿼리 발생
# ✅ prefetch_related 사용
categories = Category.objects.prefetch_related('posts') # 2개 쿼리로 해결단순 prefetch_related는 모든 관련 객체를 가져오므로 필터링된 데이터가 필요할 때 한계 존재
Prefetch 객체: 세밀한 제어
prefetch_related의 동작을 커스터마이징하여 필터링, 정렬, 슬라이싱 등을 DB 레벨에서 처리
from django.db.models import Prefetch
# 공개된 포스트만 미리 가져오기
public_posts_qs = Post.objects.filter(is_public=True)
categories = Category.objects.prefetch_related(
Prefetch('posts', queryset=public_posts_qs, to_attr='public_posts')
)
# 최근 댓글 3개만 가져오기
latest_comments_qs = Comment.objects.order_by('-created_at')
posts = Post.objects.prefetch_related(
Prefetch('comments', queryset=latest_comments_qs, to_attr='latest_comments')
)to_attr 사용으로 기존 관계와 충돌 없이 필터링된 결과를 새로운 속성에 저장
Subquery와 OuterRef: 단일 값 추출
관련 객체의 특정 값 하나를 메인 쿼리에 붙여서 가져올 때 사용
from django.db.models import Subquery, OuterRef
# 각 포스트의 가장 최근 댓글 내용 가져오기
latest_comment_subquery = Comment.objects.filter(
post=OuterRef('pk')
).order_by('-created_at').values('content')[:1]
posts = Post.objects.annotate(
latest_comment_content=Subquery(latest_comment_subquery)
)
# 각 카테고리의 공개 포스트 개수 세기
public_post_count_subquery = Post.objects.filter(
category=OuterRef('pk'),
is_public=True
).values('category').annotate(
count=Count('pk')
).values('count')
categories = Category.objects.annotate(
public_post_count=Subquery(public_post_count_subquery, output_field=IntegerField())
)OuterRef로 바깥쪽 쿼리의 필드를 참조하여 상관 서브쿼리 작성
Prefetch vs Subquery 선택 기준
구분
Prefetch
Subquery
목적
관련 객체 목록 가져오기
관련 객체의 단일 값 가져오기
결과
모델 인스턴스 리스트
새로운 필드(속성)
쿼리 수
2개
1개
사용 시기
객체들을 루프로 처리할 때
개수, 집계값, 존재 여부 확인할 때
실무 적용 전략
기본적으로 select_related와 prefetch_related 먼저 적용
필터링/정렬이 필요하면 Prefetch 객체 사용
단일 값(개수, 최신값 등)이 필요하면 Subquery 사용
django-debug-toolbar로 실행되는 SQL 쿼리 확인 필수
DRF Serializer에서 SerializerMethodField 대신 annotate 활용
Last updated
