[DRF] Pagination

강민성·2024년 9월 27일

DRF API Guide

목록 보기
18/28

Pagination (페이지네이션)

장고는 페이지가 나뉜 데이터를 관리하는 데 도움이 되는 몇 가지 클래스를 제공합니다. 이 데이터는 여러 페이지로 나뉘어 있으며, "이전/다음" 링크를 포함합니다.
— 장고 공식 문서

REST 프레임워크는 커스텀 가능한 페이지네이션 스타일을 지원합니다. 이를 통해 대규모 결과 집합을 개별 데이터 페이지로 나누는 방식을 수정할 수 있습니다.

페이지네이션 API는 다음을 지원할 수 있습니다:

  1. 응답 내용의 일부로 제공되는 페이지네이션 링크.
  2. Content-Range 또는 Link와 같은 응답 헤더에 포함된 페이지네이션 링크.

현재 기본 제공 스타일은 모두 응답 내용의 일부로 포함된 링크를 사용합니다. 이 스타일은 탐색 가능한 API를 사용할 때 더 접근성이 좋습니다.

페이지네이션은 일반 뷰 또는 뷰셋을 사용할 때 자동으로 수행됩니다. 하지만 일반 APIView를 사용하는 경우 페이지네이션 API를 직접 호출하여 페이지네이션된 응답을 반환해야 합니다. mixins.ListModelMixingenerics.GenericAPIView 클래스의 소스 코드를 참조하면 예시를 볼 수 있습니다.

페이지네이션은 페이지네이션 클래스를 None으로 설정하여 비활성화할 수 있습니다.


페이지네이션 스타일 설정

페이지네이션 스타일은 DEFAULT_PAGINATION_CLASSPAGE_SIZE 설정 키를 사용하여 전역적으로 설정할 수 있습니다. 예를 들어, 내장된 limit/offset 페이지네이션을 사용하려면 다음과 같이 설정합니다:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

페이지네이션 클래스와 사용해야 할 페이지 크기를 모두 설정해야 한다는 점에 유의하세요. 기본값으로는 DEFAULT_PAGINATION_CLASSPAGE_SIZENone으로 설정되어 있습니다.

개별 뷰에서도 pagination_class 속성을 사용하여 페이지네이션 클래스를 설정할 수 있습니다. 보통 API 전체에서 동일한 페이지네이션 스타일을 사용하지만, 기본 또는 최대 페이지 크기와 같은 개별 요소를 뷰마다 다르게 설정할 수 있습니다.


페이지네이션 스타일 수정

페이지네이션 스타일의 특정 요소를 수정하려면, 페이지네이션 클래스 중 하나를 재정의하고 변경하려는 속성을 설정하면 됩니다.

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 100
    page_size_query_param = 'page_size'
    max_page_size = 1000

그런 다음 pagination_class 속성을 사용하여 새 스타일을 뷰에 적용할 수 있습니다:

class BillingRecordsView(generics.ListAPIView):
    queryset = Billing.objects.all()
    serializer_class = BillingRecordsSerializer
    pagination_class = LargeResultsSetPagination

또는 DEFAULT_PAGINATION_CLASS 설정 키를 사용하여 전역적으로 스타일을 적용할 수 있습니다. 예를 들어:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}

API 레퍼런스

PageNumberPagination

이 페이지네이션 스타일은 요청 쿼리 파라미터에서 단일 숫자 페이지 번호를 받아옵니다.

요청 예시:

GET https://api.example.org/accounts/?page=4

응답 예시:

HTTP 200 OK
{
    "count": 1023,
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": [
       …
    ]
}

설정
PageNumberPagination 스타일을 전역적으로 활성화하려면, 다음 구성을 사용하고 필요한 PAGE_SIZE를 설정합니다:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100
}

GenericAPIView 하위 클래스에서는 pagination_class 속성을 설정하여 PageNumberPagination을 뷰마다 선택할 수 있습니다.


구성
PageNumberPagination 클래스에는 페이지네이션 스타일을 수정할 수 있는 여러 속성이 포함되어 있습니다.

이 속성들을 설정하려면 PageNumberPagination 클래스를 재정의하고, 위에서 설명한 대로 커스텀 페이지네이션 클래스를 활성화합니다.

  • django_paginator_class: 사용할 장고 Paginator 클래스입니다. 기본값은 django.core.paginator.Paginator이며 대부분의 경우 적합합니다.
  • page_size: 페이지 크기를 나타내는 숫자 값입니다. 설정되면 PAGE_SIZE 설정을 덮어씁니다. 기본값은 PAGE_SIZE 설정 키와 동일한 값입니다.
  • page_query_param: 페이지네이션 제어에 사용할 쿼리 파라미터 이름을 나타내는 문자열 값입니다.
  • page_size_query_param: 설정된 경우, 클라이언트가 요청마다 페이지 크기를 설정할 수 있도록 하는 쿼리 파라미터의 이름을 나타내는 문자열 값입니다. 기본값은 None으로, 클라이언트가 요청한 페이지 크기를 제어할 수 없음을 의미합니다.
  • max_page_size: 설정된 경우, 요청할 수 있는 최대 페이지 크기를 나타내는 숫자 값입니다. 이 속성은 page_size_query_param이 설정된 경우에만 유효합니다.
  • last_page_strings: page_query_param과 함께 마지막 페이지를 요청할 때 사용할 수 있는 문자열 값들의 목록 또는 튜플입니다. 기본값은 ('last',)입니다.
  • template: 탐색 가능한 API에서 페이지네이션 컨트롤을 렌더링할 때 사용할 템플릿의 이름입니다. 렌더링 스타일을 수정하거나 HTML 페이지네이션 컨트롤을 완전히 비활성화하려면 재정의할 수 있습니다. 기본값은 "rest_framework/pagination/numbers.html"입니다.

네, 계속해서 Django Rest Framework Pagination 문서의 나머지 부분을 번역하겠습니다.


LimitOffsetPagination

이 페이지네이션 스타일은 요청 쿼리 파라미터에서 "limit"과 "offset"을 수락합니다. 클라이언트는 처음부터 limit만큼의 항목을 가져오고, offset부터 시작하는 항목들을 건너뛸 수 있습니다.

요청 예시:

GET https://api.example.org/accounts/?limit=100&offset=400

응답 예시:

HTTP 200 OK
{
    "count": 1023,
    "next": "https://api.example.org/accounts/?limit=100&offset=500",
    "previous": "https://api.example.org/accounts/?limit=100&offset=300",
    "results": [
        ...
    ]
}

설정
LimitOffsetPagination 스타일을 전역적으로 활성화하려면 다음 구성을 사용할 수 있습니다:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

또는 GenericAPIView 하위 클래스의 pagination_class 속성을 사용하여 특정 뷰에서 이 스타일을 사용할 수 있습니다.

구성
LimitOffsetPagination 클래스는 페이지네이션 스타일을 사용자 정의할 수 있는 몇 가지 속성을 포함하고 있습니다.

  • default_limit: 기본 limit 값입니다. 페이지 크기를 설정합니다. 설정되지 않은 경우 PAGE_SIZE 설정 키가 사용됩니다.
  • limit_query_param: 클라이언트가 결과의 limit를 지정할 때 사용하는 쿼리 파라미터의 이름입니다. 기본값은 'limit'입니다.
  • offset_query_param: 클라이언트가 offset을 지정할 때 사용하는 쿼리 파라미터의 이름입니다. 기본값은 'offset'입니다.
  • max_limit: 클라이언트가 요청할 수 있는 최대 limit 값입니다. 이 속성은 None으로 설정하면 비활성화됩니다.
  • template: 탐색 가능한 API에서 페이지네이션 컨트롤을 렌더링할 때 사용할 템플릿의 이름입니다. 기본값은 "rest_framework/pagination/limit_offset.html"입니다.

CursorPagination

이 페이지네이션 스타일은 기본적으로 사용자가 이전 또는 다음 페이지를 탐색하는 데 사용할 수 있는 커서를 반환합니다. 커서 페이지네이션은 정렬된 데이터 세트에서 데이터를 일정하게 가져오는 데 유용합니다. cursor는 이전에 반환된 페이지 끝의 마지막 항목을 기준으로 페이지네이션을 수행합니다.

요청 예시:

GET https://api.example.org/accounts/?cursor=cD0yMDIyLTA1LTAzKzEyJTNBNTMlM0EwNS4wODU3NDYlMkIwMCUzQTAw

응답 예시:

HTTP 200 OK
{
    "next": "https://api.example.org/accounts/?cursor=cD0yMDIyLTA1LTAzKzEyJTNBNTMlM0EwNS4wODU3NDYlMkIwMCUzQTAw",
    "previous": null,
    "results": [
        ...
    ]
}

설정
CursorPagination 스타일을 전역적으로 활성화하려면 다음과 같이 구성할 수 있습니다:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
    'PAGE_SIZE': 100
}

또는 GenericAPIView 하위 클래스의 pagination_class 속성을 사용하여 특정 뷰에서 이 스타일을 사용할 수 있습니다.


구성
CursorPagination 클래스에는 페이지네이션 스타일을 수정할 수 있는 몇 가지 속성이 있습니다.

  • page_size: 기본 페이지 크기를 나타내는 정수입니다. 설정되면 PAGE_SIZE 설정을 덮어씁니다.
  • cursor_query_param: 커서를 지정할 때 사용하는 쿼리 파라미터의 이름입니다. 기본값은 'cursor'입니다.
  • ordering: 커서 페이지네이션에 사용할 기본 정렬 필드를 나타내는 문자열 또는 필드 목록입니다. 기본값은 '-created'입니다.
  • template: 탐색 가능한 API에서 페이지네이션 컨트롤을 렌더링할 때 사용할 템플릿의 이름입니다. 기본값은 "rest_framework/pagination/limit_offset.html"입니다.

커스텀 페이지네이션 스타일

커스텀 페이지네이션 시리얼라이저 클래스(custom pagination serializer class)를 작성하려면, pagination.BasePagination 클래스를 상속하고 paginate_queryset(self, queryset, request, view=None)get_paginated_response(self, data) 메서드를 오버라이드해야 합니다:

  • paginate_queryset 메서드는 초기 쿼리셋을 전달받아 반복 가능한 객체(iterable object)를 반환해야 합니다. 이 객체는 요청된 페이지의 데이터만 포함해야 합니다.
  • get_paginated_response 메서드는 직렬화된 페이지 데이터를 전달받아 Response 인스턴스를 반환해야 합니다.

paginate_queryset 메서드는 나중에 get_paginated_response 메서드에서 사용할 수 있는 상태(state)를 페이지네이션 인스턴스에 설정할 수 있습니다.


예시

기본 페이지네이션 출력 스타일을 수정된 형식으로 교체하여 nextprevious 링크를 중첩된 'links' 키 아래에 포함시키고자 한다고 가정해 보겠습니다. 다음과 같이 사용자 정의 페이지네이션 클래스를 지정할 수 있습니다:

from rest_framework import pagination
from rest_framework.response import Response

class CustomPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({
            'links': {
                'next': self.get_next_link(),
                'previous': self.get_previous_link()
            },
            'count': self.page.paginator.count,
            'results': data
        })

그런 다음, 구성에서 사용자 정의 클래스를 설정해야 합니다:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.CustomPagination',
    'PAGE_SIZE': 100
}

주의 사항: 응답의 키 순서 표시 방식에 신경을 쓴다면, 탐색 가능한 API에서 페이지네이션된 응답 본문을 구성할 때 OrderedDict를 사용하는 것을 고려할 수 있지만, 이는 선택 사항입니다.


커스텀 페이지네이션 클래스 사용하기

사용자 정의 페이지네이션 클래스가 기본으로 사용되도록 설정하려면 DEFAULT_PAGINATION_CLASS 설정을 사용하세요:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',
    'PAGE_SIZE': 100
}

이렇게 설정하면 리스트 엔드포인트에 대한 API 응답이 이제 응답 본문에 페이지네이션 링크를 포함하는 대신, 예를 들어 Link 헤더를 포함하게 됩니다:

Link Header

Link 헤더를 사용하는 사용자 정의 페이지네이션 스타일


HTML 페이지네이션 컨트롤

기본적으로 페이지네이션 클래스를 사용하면 탐색 가능한 API에서 HTML 페이지네이션 컨트롤이 표시됩니다. 두 가지 기본 제공 표시 스타일이 있습니다:

  • PageNumberPaginationLimitOffsetPagination 클래스: 이전 및 다음 컨트롤이 있는 페이지 번호 목록을 표시합니다.
  • CursorPagination 클래스: 이전 및 다음 컨트롤만 표시하는 더 간단한 스타일을 표시합니다.

컨트롤 커스터마이징

HTML 페이지네이션 컨트롤을 렌더링하는 템플릿을 오버라이드할 수 있습니다. 두 가지 기본 제공 스타일은 다음과 같습니다:

  • rest_framework/pagination/numbers.html
  • rest_framework/pagination/previous_and_next.html

글로벌 템플릿 디렉토리에 이 경로 중 하나의 템플릿을 제공하면 해당 페이지네이션 클래스의 기본 렌더링을 오버라이드할 수 있습니다.

또는 기존 클래스 중 하나를 서브클래싱하여 template = None 속성을 설정함으로써 HTML 페이지네이션 컨트롤을 완전히 비활성화할 수 있습니다. 그런 다음 DEFAULT_PAGINATION_CLASS 설정 키를 사용하여 사용자 정의 클래스를 기본 페이지네이션 스타일로 설정해야 합니다.


저수준 API

페이지네이션 클래스가 컨트롤을 표시해야 하는지 여부를 결정하는 저수준 API는 페이지네이션 인스턴스의 display_page_controls 속성으로 노출됩니다. HTML 페이지네이션 컨트롤이 표시되어야 하는 경우 사용자 정의 페이지네이션 클래스는 paginate_queryset 메서드에서 True로 설정되어야 합니다.

또한, 사용자 정의 페이지네이션 클래스에서 .to_html().get_html_context() 메서드를 오버라이드하여 컨트롤이 렌더링되는 방식을 추가로 커스터마이징할 수 있습니다.


외부 패키지

다음과 같은 외부 패키지도 사용 가능합니다:

DRF-extensions

DRF-extensions 패키지는 API 클라이언트가 ?page_size=max를 지정하여 최대 허용 페이지 크기를 얻을 수 있는 PaginateByMaxMixin 믹스인 클래스를 포함합니다.

drf-proxy-pagination

drf-proxy-pagination 패키지는 쿼리 파라미터를 사용하여 페이지네이션 클래스를 선택할 수 있는 ProxyPagination 클래스를 포함합니다.

django-rest-framework-link-header-pagination 패키지는 GitHub REST API 문서에 설명된 대로 HTTP Link 헤더를 통해 페이지네이션을 제공하는 LinkHeaderPagination 클래스를 포함합니다.

Reference

DRF API Guide - Pagination

profile
Back-end Junior Developer

0개의 댓글