서론: 두 시리얼라이저의 역할
Django REST Framework(DRF)에서 Serializer와 ModelSerializer는 데이터 직렬화와 검증을 담당하지만, 그 설계 목적과 사용 방식은 근본적으로 다릅니다. 이 글에서는 두 클래스의 차이점을 명확히 정리하고, 실제 프로젝트에서 어떻게 선택하고 적용해야 하는지에 대해 심층적으로 설명합니다.
기본 구조 비교
| 특징 | Serializer | ModelSerializer |
|---|---|---|
| 기반 구조 | 일반 파이썬 데이터를 위한 범용 시리얼라이저 | Django 모델과 직접 연결된 특수화된 시리얼라이저 |
| 필드 생성 | 모든 필드를 수동으로 선언해야 함 | 모델 필드를 기반으로 자동 생성 |
| CRUD 메서드 | create(), update() 수동 구현 필요 |
기본 저장 로직 자동 제공 |
| 유지보수성 | 모델 변경 시 수동 동기화 필요 | 모델 변경에 자동 반영 |
| 적합한 상황 | 비모델 데이터, 복잡한 입력 처리 | 표준적인 모델 기반 API |
Serializer: 완전한 제어가 필요한 경우
모델과 직접 연결되지 않은 데이터나, 특정 형식의 입력을 처리할 때 사용됩니다. 예를 들어 파일 업로드 요청이나 통계 리포트 생성 폼 등에 적합합니다.
from rest_framework import serializers
class FileUploadRequestSerializer(serializers.Serializer):
upload_file = serializers.FileField(help_text="CSV 또는 XLSX 파일만 허용")
process_mode = serializers.ChoiceField(
choices=[('append', '추가'), ('replace', '대체')],
default='append'
)
notify_user = serializers.BooleanField(required=False, default=True)
def validate_upload_file(self, file):
if file.size > 10 * 1024 * 1024:
raise serializers.ValidationError("파일 크기는 10MB를 초과할 수 없습니다.")
if not file.name.endswith(('.csv', '.xlsx')):
raise serializers.ValidationError("지원하지 않는 파일 형식입니다.")
return file
이처럼 Serializer는 입력 데이터의 유효성 검사와 변환에 집중할 수 있도록 설계되어, 비즈니스 로직과 밀접한 폼 형태의 요청 처리에 이상적입니다.
ModelSerializer: 모델 기반 API의 기본 선택
데이터베이스 모델과 일대일로 매핑되는 API 엔드포인트를 만들 때 가장 많이 사용됩니다. 모델의 필드를 자동으로 추출하고, 기본적인 생성/수정 로직을 제공하여 중복 코드를 줄입니다.
from .models import Task
from rest_framework import serializers
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'status', 'priority', 'due_date', 'created_at']
read_only_fields = ['created_at']
extra_kwargs = {
'priority': {'required': False},
'status': {'default': 'pending'}
}
또한, 추가 계산 필드를 포함하는 응답도 가능합니다:
class TaskDetailSerializer(serializers.ModelSerializer):
days_remaining = serializers.SerializerMethodField()
assignee_name = serializers.CharField(source='assignee.get_full_name', read_only=True)
class Meta:
model = Task
exclude = ['is_deleted'] # 특정 필드 제외
def get_days_remaining(self, obj):
if not obj.due_date:
return None
return (obj.due_date - timezone.now().date()).days
실제 프로젝트에서의 사용 전략
다음은 일반적인 DRF 프로젝트에서의 사용 비율과 추천 가이드입니다.
- ModelSerializer (80%): 대부분의 CRUD API 엔드포인트
- 확장된 ModelSerializer (15%): 계산 필드, 관계 필드 포함 응답
- Serializer (5%): 파일 처리, 검색 조건, 배치 작업 설정 등
권장 사례: 뷰셋과의 결합
from rest_framework import viewsets
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.filter(is_deleted=False)
serializer_class = TaskSerializer
고급 활용: 여러 모델 결합 응답
대시보드와 같이 여러 소스의 데이터를 통합할 때는 Serializer를 사용합니다.
class DashboardSummarySerializer(serializers.Serializer):
total_tasks = serializers.IntegerField()
pending_tasks = serializers.IntegerField()
overdue_tasks = serializers.IntegerField()
team_performance = serializers.DictField()
recent_updates = serializers.ListField(child=serializers.CharField())
결론 및 권장 사항
DRF 개발 시 다음 원칙을 따르는 것이 효율적입니다:
- 기본은 ModelSerializer: 모든 모델 기반 API는 처음부터
ModelSerializer로 시작하세요. - 필요 시 확장: 출력에 계산 값이 필요하면
SerializerMethodField로 보완하세요. - 자체 구현은 마지막 선택:
Serializer는 모델과 무관한 입력이나 복잡한 검증이 있을 때만 사용하세요. - 명시적 필드 선언: 프로덕션 환경에서는
fields = '__all__'보다는 명시적으로 필드를 나열하세요.
이러한 접근은 개발 속도와 유지보수성을 동시에 확보할 수 있는 최적의 실무 패턴입니다.