
웹 개발에서 API(Application Programming Interface)는 마치 레스토랑의 메뉴판과 같습니다. 고객(클라이언트)이 원하는 음식(데이터)을 주문할 수 있는 방법을 제공하죠. Django REST Framework(DRF)의 ListAPIView는 이러한 API 중에서도 특히 "목록을 보여주는" 기능을 담당하는 강력한 도구입니다.
ListAPIView는 Django REST Framework에서 제공하는 제네릭 뷰(Generic View) 중 하나입니다. 이름에서 알 수 있듯이, 데이터베이스에 저장된 여러 개의 객체들을 목록 형태로 조회하는 기능을 제공합니다.
온라인 쇼핑몰을 생각해보세요:
먼저 Django REST Framework가 설치되어 있어야 합니다:
pip install djangorestframework
settings.py에 DRF를 추가:
INSTALLED_APPS = [
# ... 기존 앱들
'rest_framework',
'your_app',
]
블로그 포스트를 예시로 사용하겠습니다:
# models.py
from django.db import models
from django.contrib.auth.models import User
class BlogPost(models.Model):
title = models.CharField(max_length=200, verbose_name="제목")
content = models.TextField(verbose_name="내용")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="작성자")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="작성일")
updated_at = models.DateTimeField(auto_now=True, verbose_name="수정일")
is_published = models.BooleanField(default=True, verbose_name="공개 여부")
class Meta:
ordering = ['-created_at'] # 최신순 정렬
verbose_name = "블로그 포스트"
verbose_name_plural = "블로그 포스트들"
def __str__(self):
return self.title
시리얼라이저는 번역기 역할을 합니다. 파이썬 객체를 JSON으로, JSON을 파이썬 객체로 변환해줍니다:
# serializers.py
from rest_framework import serializers
from .models import BlogPost
class BlogPostSerializer(serializers.ModelSerializer):
author_name = serializers.CharField(source='author.username', read_only=True)
class Meta:
model = BlogPost
fields = ['id', 'title', 'content', 'author_name', 'created_at', 'updated_at', 'is_published']
read_only_fields = ['created_at', 'updated_at']
이제 핵심인 ListAPIView를 구현해보겠습니다:
# views.py
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from .models import BlogPost
from .serializers import BlogPostSerializer
class BlogPostListView(ListAPIView):
"""
블로그 포스트 목록을 제공하는 API View
"""
queryset = BlogPost.objects.filter(is_published=True)
serializer_class = BlogPostSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['author', 'is_published']
search_fields = ['title', 'content']
ordering_fields = ['created_at', 'updated_at', 'title']
ordering = ['-created_at'] # 기본 정렬: 최신순
# urls.py
from django.urls import path
from .views import BlogPostListView
urlpatterns = [
path('api/posts/', BlogPostListView.as_view(), name='blog-post-list'),
]
때로는 기본 필터링으로는 부족할 수 있습니다:
from rest_framework.generics import ListAPIView
from django.db.models import Q
from datetime import datetime, timedelta
class BlogPostListView(ListAPIView):
serializer_class = BlogPostSerializer
def get_queryset(self):
"""
커스텀 쿼리셋 로직
"""
queryset = BlogPost.objects.filter(is_published=True)
# URL 파라미터 처리
author_id = self.request.query_params.get('author_id')
if author_id:
queryset = queryset.filter(author_id=author_id)
# 최근 포스트만 필터링
recent = self.request.query_params.get('recent')
if recent == 'true':
week_ago = datetime.now() - timedelta(days=7)
queryset = queryset.filter(created_at__gte=week_ago)
# 키워드 검색
keyword = self.request.query_params.get('keyword')
if keyword:
queryset = queryset.filter(
Q(title__icontains=keyword) |
Q(content__icontains=keyword)
)
return queryset
대량의 데이터를 효율적으로 처리하기 위해 페이지네이션을 설정합니다:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
커스텀 페이지네이션:
# pagination.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class CustomPageNumberPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'page_size'
max_page_size = 100
def get_paginated_response(self, data):
return Response({
'pagination': {
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'count': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'page_size': self.page_size
},
'results': data
})
# views.py에서 사용
class BlogPostListView(ListAPIView):
queryset = BlogPost.objects.filter(is_published=True)
serializer_class = BlogPostSerializer
pagination_class = CustomPageNumberPagination
누가 API에 접근할 수 있는지 제어합니다:
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
class BlogPostListView(ListAPIView):
queryset = BlogPost.objects.filter(is_published=True)
serializer_class = BlogPostSerializer
permission_classes = [IsAuthenticatedOrReadOnly] # 읽기는 누구나, 쓰기는 인증된 사용자만
성능 향상을 위해 캐싱을 적용할 수 있습니다:
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
@method_decorator(cache_page(60 * 15), name='get') # 15분 캐싱
class BlogPostListView(ListAPIView):
queryset = BlogPost.objects.filter(is_published=True)
serializer_class = BlogPostSerializer
위에서 만든 API를 호출하면 다음과 같은 JSON 응답을 받을 수 있습니다:
{
"pagination": {
"next": "http://localhost:8000/api/posts/?page=2",
"previous": null,
"count": 45,
"total_pages": 3,
"current_page": 1,
"page_size": 20
},
"results": [
{
"id": 1,
"title": "Django REST Framework 시작하기",
"content": "Django REST Framework는 강력한 웹 API 프레임워크입니다...",
"author_name": "김개발",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"is_published": true
},
{
"id": 2,
"title": "Python 기초 문법",
"content": "Python은 배우기 쉬운 프로그래밍 언어입니다...",
"author_name": "이프로그래머",
"created_at": "2024-01-14T15:45:00Z",
"updated_at": "2024-01-14T15:45:00Z",
"is_published": true
}
]
}
class BlogPostListView(ListAPIView):
serializer_class = BlogPostSerializer
def get_queryset(self):
category = self.kwargs.get('category')
if category:
return BlogPost.objects.filter(category=category, is_published=True)
return BlogPost.objects.filter(is_published=True)
# urls.py
path('api/posts/category/<str:category>/', BlogPostListView.as_view())
class AuthorPostListView(ListAPIView):
serializer_class = BlogPostSerializer
def get_queryset(self):
author_id = self.kwargs['author_id']
return BlogPost.objects.filter(author_id=author_id, is_published=True)
# urls.py
path('api/authors/<int:author_id>/posts/', AuthorPostListView.as_view())
class BlogPostListView(ListAPIView):
# select_related로 연관 객체 한 번에 조회
queryset = BlogPost.objects.select_related('author').filter(is_published=True)
serializer_class = BlogPostSerializer
from rest_framework.exceptions import ValidationError
class BlogPostListView(ListAPIView):
serializer_class = BlogPostSerializer
def get_queryset(self):
try:
return BlogPost.objects.filter(is_published=True)
except Exception as e:
raise ValidationError(f"데이터를 불러오는 중 오류가 발생했습니다: {str(e)}")
class BlogPostListView(ListAPIView):
serializer_class = BlogPostSerializer
def get_queryset(self):
# 민감한 정보가 포함된 필드는 제외
return BlogPost.objects.filter(is_published=True).exclude(is_draft=True)
ListAPIView는 Django REST Framework에서 목록 조회 API를 구현하는 가장 간단하고 효율적인 방법입니다. 기본적인 CRUD 작업 중 'Read' 기능을 담당하며, 필터링, 검색, 정렬, 페이지네이션 등 다양한 기능을 쉽게 구현할 수 있습니다.
핵심 포인트:
ListAPIView를 마스터하면 웹 API 개발에서 목록 조회 기능을 빠르고 안정적으로 구현할 수 있습니다. 다음 단계로는 CreateAPIView, UpdateAPIView, DestroyAPIView 등 다른 제네릭 뷰들도 학습해보시기 바랍니다.