본문 바로가기
Web/Django

[DRF] 4. Pagination / Filtering & Search

by 조엘 2021. 1. 15.

안녕하세요! 파피몬입니다! 🏆

백엔드 API 서버가 어떻게 구축되는지 조금 알 필요가 있다고 생각이 들어서 Django Rest Framework(이하 DRF)를 공부하고 있습니다. 아직 개발자를 지망하는 학생입니다! 틀린 부분이 있으면 댓글로 알려주시면 정말 감사하겠습니다!!

 

 

*** 개요 ***

이번엔 Pagination과 Filtering & Search에 대해 알아보자. 이 둘은 client 측에서 필요한 만큼의 정보를 알맞게 전달해주는 것을 지원한다. 기능 구현 자체는 별로 어렵지는 않다. 한 번 알아보도록 하자!

 

 

*** Pagination ***

데이터를 저장하는 API 서버에서 Pagination이 필요한 이유는 무엇일까? 그 이유는 하나의 request 요청에 따르는 수많은 데이터여러 request로 나누어 client에게 전송해 줘야 하는 경우가 있기 때문이다. 

 

예를 들어 데이터 목록을 요청하는 request에 따라, 그동안 저장돼있던 서버의 2억 개의 데이터를 response로 보낸다고 생각해보자. 이 만큼의 정보는 client단에서 다 보여줄 수 있지도, 필요하지도 않다. 나누어서 조금씩 보여주고, 더 필요하다면 추가적으로 요청을 보내라고 하는 게 더 아름다운 그림일 것이다. 

 

여기서 한 가지 고려해야 할 사항이 있다면, 어떤 기준으로 데이터를 나누어서 보여주는지를 정해야 한다는 점이다. 이는 개발자의 구현 방법에 따라 최신 정보 먼저, 가나다 순으로 등 여러 가지 정렬 방법이 있을 수 있다. 확실한 것은 일정한 기준으로 정렬을 먼저 한 후에 조금씩 나누어 보여주도록 한다는 점이다. 

 

DRF Pagination 공식문서 (참고: www.django-rest-framework.org/api-guide/pagination/)에 따르면 DRF에서는 PageNumberPagination / LimitOffsetPagination / CursorPagination을 제공한다. 여기에 개발자의 입맛에 맞는 Pagination을 구현할 수 있도록 Custom Pagination을 제공한다. 

 

어떠한 방식으로 url request를 보내야 하는지는 공식 문서에 작성되어 있으니 생략하고, 해당 포스팅에서는 구현에 초점을 맞추어 PageNumberPagination을 통한 구현을 소개한다. 

 

전역 설정을 통한 Pagination

DRF의 모델들에 전반적으로 같은 Pagination 방식을 적용하고 싶다면, 해당 방식을 사용하도록 하자. Django 프로젝트의 setting.py에 간단한 설정을 추가해 주면 PageNumberPagination을 구현할 수 있다. 

REST_FRAMEWORK라는 딕셔너리를 추가하여, rest_framework.settings내의 설정들을 key 값으로 적용하면 된다. 

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 5,
}

 

단일 모델을 위한 Pagination

특정한 모델에 대해 다른 pagination을 적용하고 싶은 경우, 이 역시 가능하다. 아래와 같이 따로 pagination에 대한 설정을 pagination.py와 같이 다른 파일로 만들어 준 후, 이를 ViewSet을 상속받고 있는 class에 pagination_class 변수에 대응하는 클래스로 지정해 주면 된다. 

# pagination.py
from rest_framework.pagination import PageNumberPagination

class MyPagination(PageNumberPagination):
    page_size = 3

 

Pagination을 지정한 후 "~/post/"로 요청을 보내면, 다음과 같이 json 응답을 하게 된다.

Pagination 이전에는 데이터 목록들이 주르륵 json 응답으로 돌아온 반면, 지금은 "count", "next", "previous", "result" 총 4가지로 나뉘어 json 응답을 해주는 것을 볼 수 있다. 이와 같이 넘어온 적당히 잘린 데이터는 client단에서 예쁘게 보여줄 수 있고, 다음 데이터가 필요한 경우 API 서버에 "next"에 해당하는 url로 요청을 보내면 될 것이다.

Django에서 pagination을 위해 페이지 자체를 새로고침 했던 것을 생각하면, SPA 프레임워크(아직 배우진 않았지만ㅜ)에서 API 서버로 request만 보내 페이지 새로고침 없이 pagination을 구현할 수 있게 되었다. 

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 5,
    "next": "http://127.0.0.1:8000/post/?page=2",
    "previous": null,
    "results": [
        {
            "id": 1,
            "title": "Hello",
            "body": "World"
        },
        {
            "id": 2,
            "title": "아하",
            "body": "이렇게 JSON을 쓰는구만"
        },
        {
            "id": 3,
            "title": "좋은",
            "body": "예감"
        }
    ]
}

 

 

*** Filtering & Search ***

현재 DRF 공부를 멋사의 강민철 님 강의로 하고 있는데(강의 감사드려요 민철님!) 해당 강의에서 FilteringRequest를 걸러 보내기, SearchResponse를 걸러 받기로 구분한다. 처음엔 조금 와 닿지 않았는데, 예시를 통해 이해를 할 수 있었다. 예시와 함께, 구현 방법까지 알아보도록 하자. 

 

Filtering

Request를 걸러 보내는 Filtering의 예시는 다음과 같다. 예를 들어 한 유저가 로그인을 하면, 자신이 작성한 글을 조회할 수 있는 페이지가 있다고 가정해보자. 이를 어찌 구현할 수 있을까? 다음과 같은 로직이 적용 가능하다.

 - Request 보낸 유저가 등록이 되어 있다면, 해당 유저가 작성한 글만 필터링해서 response 해준다. 

 - Request 보낸 유저가 등록이 안 되어 있다면, 빈 리스트를 response 해준다. 

 

Filtering은 views.py에서 GenericAPIView의 get_queryset()을 오버라이딩 함으로써 구현한다. queryset의 filter를 이용하여 원하는 데이터만을 response 해줄 수 있게 되었다. 

# views.py (참고: https://www.django-rest-framework.org/api-guide/filtering/)
from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return Purchase.objects.filter(purchaser=user)
        else: 
            return Purchase.objects.none()

 

Search

Response를 걸러 받는 Search의 예시는 다음과 같다. 예를 들어 현재 내가 쓴 글들의 목록을 전달받았다고 하자. 이 중에 title column에 "안녕" 이 포함된 글들을 검색하고 싶다. 이와 같은 검색 기능은 rest_framework.filters의 SearchFilter를 통해 쉽게 구현할 수 있다. 

# views.py
...
from rest_framework.filters import SearchFilter

class UserPostViewSet(viewsets.ModelViewSet):
    queryset = UserPost.objects.all()
    serializer_class = UserSerializer

    # SearchFilter 기반으로 검색할 예정입니다
    filter_backends = [SearchFilter]
    # 어떤 칼럼을 기반으로 검색을 할 건지 search_fields에 *튜플* 형식으로 적어주세요
    search_fields = ('title', 'body',)

 

이후 url에 "/userpost/? search=안녕"으로 쿼리 스트링을 추가해 요청을 보내면, 다음과 같이 "안녕"이 포함된 글에 대해 검색이 가능해진다. 검색 기능 구현 완료!

"안녕" 에 대한 response

 

 

반응형

'Web > Django' 카테고리의 다른 글

[DRF] 5. Authentication / Permission  (0) 2021.01.15
[DRF] 3. ViewSet / Router  (0) 2021.01.14
[DRF] 2. APIView / Mixins / Generic CBV  (0) 2021.01.13
[DRF] 1. 개요 / REST / JSON / Serializer  (0) 2021.01.13
[Django] 자소설 닷컴 총 복습!  (0) 2020.08.29

댓글