Django REST Framework ListAPIView 완벽 가이드

Hyeonio_o·2025년 7월 16일

BackEnd

목록 보기
6/9
post-thumbnail

🔍 들어가며

웹 개발에서 API(Application Programming Interface)는 마치 레스토랑의 메뉴판과 같습니다. 고객(클라이언트)이 원하는 음식(데이터)을 주문할 수 있는 방법을 제공하죠. Django REST Framework(DRF)의 ListAPIView는 이러한 API 중에서도 특히 "목록을 보여주는" 기능을 담당하는 강력한 도구입니다.

ListAPIView란 무엇인가?

ListAPIView는 Django REST Framework에서 제공하는 제네릭 뷰(Generic View) 중 하나입니다. 이름에서 알 수 있듯이, 데이터베이스에 저장된 여러 개의 객체들을 목록 형태로 조회하는 기능을 제공합니다.

실생활 예시로 이해하기

온라인 쇼핑몰을 생각해보세요:

  • 상품 목록 페이지 → ListAPIView가 상품 데이터들을 JSON 형태로 제공
  • 게시글 목록 페이지 → ListAPIView가 게시글 데이터들을 제공
  • 사용자 목록 페이지 → ListAPIView가 사용자 데이터들을 제공

기본 사용법

1. 기본 설정

먼저 Django REST Framework가 설치되어 있어야 합니다:

pip install djangorestframework

settings.py에 DRF를 추가:

INSTALLED_APPS = [
    # ... 기존 앱들
    'rest_framework',
    'your_app',
]

2. 모델 생성

블로그 포스트를 예시로 사용하겠습니다:

# 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

3. 시리얼라이저 생성

시리얼라이저는 번역기 역할을 합니다. 파이썬 객체를 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']

4. ListAPIView 구현

이제 핵심인 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']  # 기본 정렬: 최신순

5. URL 연결

# urls.py
from django.urls import path
from .views import BlogPostListView

urlpatterns = [
    path('api/posts/', BlogPostListView.as_view(), name='blog-post-list'),
]

고급 기능들

1. 커스텀 필터링

때로는 기본 필터링으로는 부족할 수 있습니다:

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

2. 페이지네이션 설정

대량의 데이터를 효율적으로 처리하기 위해 페이지네이션을 설정합니다:

# 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

3. 권한 설정

누가 API에 접근할 수 있는지 제어합니다:

from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly

class BlogPostListView(ListAPIView):
    queryset = BlogPost.objects.filter(is_published=True)
    serializer_class = BlogPostSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]  # 읽기는 누구나, 쓰기는 인증된 사용자만

4. 캐싱 적용

성능 향상을 위해 캐싱을 적용할 수 있습니다:

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 응답 예시

위에서 만든 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
        }
    ]
}

다양한 사용 예시

1. 카테고리별 필터링

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())

2. 특정 작성자의 포스트만 조회

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())

주의사항 및 베스트 프랙티스

1. 성능 최적화

class BlogPostListView(ListAPIView):
    # select_related로 연관 객체 한 번에 조회
    queryset = BlogPost.objects.select_related('author').filter(is_published=True)
    serializer_class = BlogPostSerializer

2. 에러 처리

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)}")

3. 보안 고려사항

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' 기능을 담당하며, 필터링, 검색, 정렬, 페이지네이션 등 다양한 기능을 쉽게 구현할 수 있습니다.

핵심 포인트:

  • 간단한 구현: 몇 줄의 코드로 완전한 API 구현 가능
  • 높은 확장성: 커스텀 로직 추가 용이
  • 풍부한 기능: 필터링, 검색, 정렬, 페이지네이션 내장
  • 성능 최적화: 캐싱, 쿼리 최적화 등 다양한 최적화 기법 적용 가능

ListAPIView를 마스터하면 웹 API 개발에서 목록 조회 기능을 빠르고 안정적으로 구현할 수 있습니다. 다음 단계로는 CreateAPIView, UpdateAPIView, DestroyAPIView 등 다른 제네릭 뷰들도 학습해보시기 바랍니다.

0개의 댓글