IT/작업기록

[Django] Prefetch는 어떤 역할을 하는가

aram 2026. 4. 20. 16:47

Q. prefetch_related는 알겠는데, Prefetch 객체는 왜 따로 쓰는가?

prefetch_related만 쓰면 관련 객체를 전부 가져온다. 하지만 관련 객체 중 일부만(예: 삭제되지 않은 항목만) 가져오고 싶을 때는 그 조건을 명시할 방법이 없다. 이때 Prefetch 객체를 쓰면, "이 관계를 prefetch할 때 어떤 queryset을 쓸지"를 직접 지정할 수 있다.

예를 들어 상세 항목 중 is_deleted=False인 것만 표시하고 싶다면, Prefetch 객체로 queryset을 명시한다.

# Mixin의 필터/검색/정렬 적용 후 is_deleted 필터 추가
# 관련 데이터(user, group, state)도 함께 조회(select_related)하여 성능 최적화
def get_queryset(self):
    active_details = QuotationDetail.objects.filter(is_deleted=False).select_related("prod")
    queryset = (
        super()
        .get_queryset()
        .filter(is_deleted=False)
        .select_related("user", "user__group", "state")
        .prefetch_related(Prefetch("quo_details", queryset=active_details))
    )

    # 상태 필터
    state_filter = self.request.GET.get("state_filter", "")
    if state_filter:
        queryset = queryset.filter(state__state_id=state_filter)

    return queryset

정리

  • select_related: 정방향 관계(ForeignKey, OneToOne)를 SQL JOIN으로 한 번에 가져온다. 여기서는 user, user__group, state.
  • prefetch_related: 역방향/다대다 관계를 별도 쿼리로 가져와서 Python 레벨에서 합쳐준다. 여기서는 quo_details.
  • Prefetch(관계이름, queryset=...): prefetch_related가 가져올 관련 객체의 범위를 직접 지정한다. 여기서는 quo_details 중 삭제되지 않은 것만, 그리고 그 안에서도 prod를 미리 select_related해서 N+1 쿼리를 한 번 더 줄였다.

즉 이 코드는 "목록 화면에서 삭제되지 않은 항목과 그 상세(삭제되지 않은 것만) + 연관 데이터를 최소한의 쿼리로 가져오기" 위한 최적화다. Prefetch 없이 그냥 prefetch_related("quo_details")만 썼다면, 삭제된 상세 항목까지 다 가져온 뒤 템플릿이나 코드에서 걸러내야 했을 것이다.