Django REST Framework

Jindolph·2024년 8월 5일

Django REST Framework(DRF) 핵심 개념 및 개발 순서

이 내용은, 자바 + 스프링에 익숙한 개발자 시각으로 보는 DRF 입니다.

1. DRF의 핵심 개념 및 구성 요소

Django REST framework(DRF)는 Django 기반의 강력한 RESTful API 프레임워크입니다. DRF의 주요 구성 요소와 아키텍처는 다음과 같습니다:

  • ViewSets: 모델의 CRUD 작업을 처리하는 클래스 기반의 뷰입니다. ModelViewSet은 CRUD 작업을 자동으로 처리합니다.
  • Serializers: Django 모델을 JSON 등으로 변환하는 역할을 합니다. ModelSerializer를 사용하여 모델 기반 시리얼라이저를 쉽게 생성할 수 있습니다.
  • Routers: ViewSets와 URL 패턴을 자동으로 매핑합니다. DefaultRouter를 사용하여 URL을 자동으로 생성할 수 있습니다.
  • Authentication 및 Permissions: 다양한 인증 방식(기본 인증, 토큰 인증, JWT 인증 등)과 권한 시스템을 통해 요청에 대한 접근 권한을 제어합니다.
  • Querysets 및 Managers: 데이터베이스에서 데이터를 쿼리하고, 모델에 대한 데이터베이스 쿼리 로직을 캡슐화합니다.
  • Validation: 입력 데이터의 유효성을 검증합니다.
  • Pagination: 기본적인 페이징 기능을 제공합니다.
  • Throttling: 요청 속도 제한 기능을 제공합니다.

2. 보편적인 개발 순서

DRF를 사용한 RESTful API 개발의 일반적인 순서는 다음과 같습니다:
Spring의 Model -> Repository -> Service -> Controller 순서와 유사.

  1. 모델 정의 (models.py)
  2. 쿼리셋 정의 (필요한 경우)
  3. 시리얼라이저 정의 (serializers.py)
  4. 뷰셋 정의 (views.py)
  5. 라우터 정의 (urls.py)

2.1 모델 정의 (models.py)

Django의 모델은 데이터베이스 스키마를 정의하고, 데이터베이스 테이블과 매핑됩니다.

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    bio = models.TextField(blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

2.2 쿼리셋 정의 (필요한 경우)

쿼리셋은 데이터베이스에서 데이터를 가져오는 데 사용됩니다. 일반적으로 모델 매니저를 통해 정의합니다.

from django.db import models

class UserManager(models.Manager):
    def active_users(self):
        return self.filter(is_active=True)

class User(AbstractUser):
    bio = models.TextField(blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

    objects = UserManager()

2.3 시리얼라이저 정의 (serializers.py)

시리얼라이저는 Django 모델 인스턴스를 JSON 등으로 직렬화하거나 역직렬화하는 데 사용됩니다.
Spring MVC에서는 Jackson ObjectMapper 등의 역할.

from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name', 'bio', 'location', 'birth_date']

    def validate_email(self, value):
        if not value.endswith('@example.com'):
            raise serializers.ValidationError("Email must be from example.com domain")
        return value

2.4 뷰셋 정의 (views.py)

뷰셋은 기본적인 CRUD 작업을 처리하는 뷰의 모음입니다. DRF의 ModelViewSet을 사용하여 기본 CRUD 작업을 쉽게 구현할 수 있습니다.

from rest_framework import viewsets
from .models import User
from .serializers import UserSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

2.5 라우터 정의 (urls.py)

라우터는 URL 패턴과 뷰셋을 매핑합니다. DRF의 DefaultRouter를 사용하여 URL을 자동으로 생성할 수 있습니다.
아래 r'users'는 /users 경로를 라우터에 등록하겠다는 뜻.

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet

router = DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

3. 쿼리셋 정의 샘플

쿼리셋을 정의하여 데이터베이스에서 필터링된 데이터를 가져올 수 있습니다.

from django.db import models

class UserManager(models.Manager):
    def active_users(self):
        return self.filter(is_active=True)

class User(AbstractUser):
    bio = models.TextField(blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

    objects = UserManager()

4. 뷰셋에서 기본적으로 제공하는 CRUD 외, 비지니스 로직 등을 정의하는 방법

4-1. 커스텀 액션

@action 데코레이터를 사용하여 ViewSet에 커스텀 액션을 추가할 수 있습니다.

from rest_framework.decorators import action
from rest_framework.response import Response

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'], url_path='activate', url_name='activate')
    def activate_user(self, request, pk=None):
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({'status': 'user activated'})

    @action(detail=True, methods=['post'], url_path='deactivate', url_name='deactivate')
    def deactivate_user(self, request, pk=None):
        user = self.get_object()
        user.is_active = False
        user.save()
        return Response({'status': 'user deactivated'})

4-2. 서비스 계층 구현

비즈니스 로직을 서비스 계층으로 분리하여 코드의 재사용성과 유지보수성을 높일 수 있습니다.

# apps/users/services.py
from .models import User

class UserService:
    
    @staticmethod
    def activate_user(user):
        user.is_active = True
        user.save()
    
    @staticmethod
    def deactivate_user(user):
        user.is_active = False
        user.save()
# apps/users/views.py
from .services import UserService

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'], url_path='activate', url_name='activate')
    def activate_user(self, request, pk=None):
        user = self.get_object()
        UserService.activate_user(user)
        return Response({'status': 'user activated'})

    @action(detail=True, methods=['post'], url_path='deactivate', url_name='deactivate')
    def deactivate_user(self, request, pk=None):
        user = self.get_object()
        UserService.deactivate_user(user)
        return Response({'status': 'user deactivated'})

5. Serializer를 통한 create, update, validate

5-1. 정의 방법 기초

from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name', 'bio', 'location', 'birth_date']

    def validate_email(self, value):
        if not value.endswith('@example.com'):
            raise serializers.ValidationError("Email must be from example.com domain")
        return value

    def create(self, validated_data):
        return User.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.username = validated_data.get('username', instance.username)
        instance.email = validated_data.get('email', instance.email)
        instance.first_name = validated_data.get('first_name', instance.first_name)
        instance.last_name = validated_data.get('last_name', instance.last_name)
        instance.bio = validated_data.get('bio', instance.bio)
        instance.location = validated_data.get('location', instance.location)
        instance.birth_date = validated_data.get('birth_date', instance.birth_date)
        instance.save()
        return instance

5-2. 사용법

  • 데이터 유효성 검증: serializer.is_valid()
  • 객체 생성: serializer = UserSerializer(data=request.data)
  • 객체 업데이트: serializer = UserSerializer(user, data=request.data, partial=True)
  • 저장: serializer.save()
# POST 요청에서 새로운 사용자 생성
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
    serializer.save()
else:
    print(serializer.errors)

# PUT 또는 PATCH 요청에서 기존 사용자 업데이트
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
    serializer.save()
else:
    print(serializer.errors)

Serializer의 자동으로 호출되는 메서드 정의 및 사용.

  1. validate_ 메서드:
    • 특정 필드에 대한 검증 로직을 정의할 때 사용합니다.
    • 예: validate_email 메서드는 email 필드에 대한 검증을 수행합니다.
    • 이 메서드는 시리얼라이저가 데이터를 검증할 때 자동으로 호출됩니다.
  2. validate 메서드:
    • 여러 필드를 동시에 검증하거나 필드 간의 관계를 검증할 때 사용합니다.
    • 이 메서드는 is_valid 메서드가 호출될 때 자동으로 실행됩니다.
  3. create 메서드:
    • 새로운 객체를 생성할 때 호출됩니다.
    • 시리얼라이저의 save 메서드가 호출될 때, 시리얼라이저의 인스턴스가 존재하지 않으면 create 메서드가 호출됩니다.
  4. update 메서드:
    • 기존 객체를 업데이트할 때 호출됩니다.
    • 시리얼라이저의 save 메서드가 호출될 때, 시리얼라이저의 인스턴스가 존재하면 update 메서드가 호출됩니다.

참고 링크

profile
Hello World!

0개의 댓글