View 기반 API 구현 템플릿
# views.py
from django.views import View
from django.http import JsonResponse
import json
class ProjectManageView(View):
"""
GET: 전체 프로젝트 조회 (직렬화)
POST: 새 프로젝트 생성 (역직렬화)
"""
def retrieve(self, request):
"""전체 데이터 조회 및 직렬화 출력"""
# 1. ORM으로 쿼리셋 획득
queryset = Project_t.objects.all().order_by('-create_time')
# 2. instance 파라미터로 직렬화 객체 생성 (다중 객체는 many=True 필수)
serializer = serializerstest.ProjectSerializer(instance=queryset, many=True)
# 3. .data 속성으로 직렬화 결과 획득 후 응
return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii': False})
def create(self, request):
"""요청 데이터 역직렬화 및 저장"""
# 1. 바이트 스트림 → 문자열 → 파이썬 dict 변환
payload = json.loads(request.body.decode('utf-8'))
# 2. data 파라미터로 역직렬화 객체 생성 (검증용)
deserializer = serializerstest.ProjectSerializer(data=payload)
# 3. is_valid() 호출로 유효성 검증 실행
if not deserializer.is_valid(raise_exception=False):
return JsonResponse(
{'error': deserializer.errors},
status=400,
json_dumps_params={'ensure_ascii': False}
)
# 4. 검증 통과 데이터로 ORM 객체 생성
new_project = Project_t.objects.create(**deserializer.validated_data)
# 5. 생성된 객체 다시 직렬화하여 응답
response_serializer = serializerstest.ProjectSerializer(instance=new_project)
return JsonResponse(
response_serializer.data,
status=201,
json_dumps_params={'ensure_ascii': False}
)
직렬화 vs 역직렬화 파라미터 비교
| 구분 |
역직렬화 (입력) |
직렬화 (출력) |
| 핵심 파라미터 |
data=dict |
instance=Model |
| 데이터 방향 |
클라이언트 → 서버 |
서버 → 클라이언트 |
| 주요 메서드 |
.is_valid(), .validated_data |
.data |
| 검증 여부 |
필드 규칙 기반 자동 검증 |
검증 없음, 포맷 변환만 수행 |
전체 데이터 흐름도
# === 클라이언트 요청 단계 ===
# Content-Type: application/json
# Body: {"name": "AI 플랫폼", "leader": "김철수", "is_execute": true, "desc": "머신러닝 기반 서비스"}
# 1. 역직렬화: JSON → Python dict → 검증
incoming = json.loads(request.body.decode('utf-8'))
# incoming: {'name': 'AI 플랫폼', 'leader': '김철수', 'is_execute': True, 'desc': '머신러닝 기반 서비스'}
checker = ProjectSerializer(data=incoming)
checker.is_valid(raise_exception=True) # 필드 규칙, validators, 단일 필드 검증 순으로 실행
# 2. 데이터베이스 반영
project_record = Project_t.objects.create(**checker.validated_data)
# project_record: <Project_t: id=7, name='AI 플랫폼'...>
# 3. 직렬화: ORM 객체 → JSON 응답
formatter = ProjectSerializer(instance=project_record)
response_payload = formatter.data
# response_payload: {'id': 7, 'name': 'AI 플랫폼', 'leader': '김철수', 'desc': '머신러닝 기반 서비스', 'create_time': '2024-01-15 09:30:00'}
모델 정의
# models.py
from django.db import models
from utils.base_model import BaseModel
class Project_t(BaseModel):
name = models.CharField(
max_length=50,
unique=True,
verbose_name="프로젝트명",
help_text="고유한 프로젝트 이름 입력"
)
leader = models.CharField(
max_length=50,
verbose_name="담당자",
help_text="프로젝트 책임자 이름"
)
is_execute = models.BooleanField(
default=False,
verbose_name="실행 여부",
help_text="프로젝트 시작 상태 선택"
)
desc = models.TextField(
max_length=125,
blank=True,
null=True,
verbose_name="상세 설명",
help_text="프로젝트에 대한 부가 설명"
)
class Meta:
db_table = "tb_project_test"
verbose_name = "프로젝트"
ordering = ['-id']
def __str__(self):
return f'[{self.id}] {self.name}'
Serializer 상세 구현
# serializers.py
from rest_framework import serializers, validators
from . import modelstest
# ============ 커스텀 검증 함수 (모듈 레벨) ============
def check_name_has_keyword(val):
"""프로젝트명 필수 키워드 검증"""
if '플랫폼' not in val:
raise serializers.ValidationError("프로젝트명에 '플랫폼'을 포함해야 합니다")
return val
def check_leader_format(val):
"""담당자명 형식 검증"""
if len(val) < 2:
raise serializers.ValidationError("담당자명은 2자 이상이어야 합니다")
return val
# ============ Serializer 클래스 ============
class ProjectSerializer(serializers.Serializer):
"""
Serializer 사용 목적:
- ORM 객체 → JSON 변환 (직렬화)
- JSON → Python 객체 변환 및 검증 (역직렬화)
- 비즈니스 규칙을 분리된 계층에서 관리
"""
# --- 출력 전용 필드 (read_only=True) ---
id = serializers.IntegerField(
read_only=True,
min_value=1,
label='프로젝트 ID',
help_text='자동 생성되는 고유 식별자'
)
create_time = serializers.DateTimeField(
read_only=True,
format='%Y-%m-%d %H:%M:%S',
label='등록일시'
)
# --- 입출력 공통 필드 ---
name = serializers.CharField(
max_length=100,
min_length=2,
required=True,
label='프로젝트명',
help_text='2~100자 사이의 프로젝트 이름',
validators=[
check_name_has_keyword,
validators.UniqueValidator(
queryset=modelstest.Project_t.objects.all(),
message="이미 등록된 프로젝트명입니다"
)
],
error_messages={
'required': '프로젝트명은 필수 입력값입니다',
'min_length': '프로젝트명은 최소 2자 이상 입력하세요',
'max_length': '프로젝트명은 최대 100자까지 가능합니다'
}
)
leader = serializers.CharField(
max_length=100,
required=True,
allow_blank=False,
trim_whitespace=True, # 좌우 공백 자동 제거
validators=[check_leader_format],
error_messages={
'required': '당자를 지정해야 합니다',
'null': '담당자는 null일 수 없습니다'
}
)
desc = serializers.CharField(
max_length=255,
required=True,
help_text='프로젝트에 대한 상세 설명'
)
# --- 입력 전용 필드 (write_only=True) ---
is_execute = serializers.BooleanField(
write_only=True,
required=False,
default=False,
help_text='프로젝트 즉시 실행 여부 (응답 미포함)'
)
# ============ 단일 필드 검증 (validate_<field_name>) ============
def validate_name(self, value):
"""
name 필드의 모든 validators 통과 후 실행
복잡한 비즈니스 규칙을 여기에 배치
"""
if value.count(' ') > 0:
raise serializers.ValidationError("프로젝트명에 공백을 포함할 수 없습니다")
if not any(c.isdigit() for c in value):
raise serializers.ValidationError("프로젝트명에 숫자를 1개 이상 포함해야 합니다")
return value
def validate_desc(self, value):
"""desc 필드 추가 검증"""
forbidden_words = ['금지', '불가']
for word in forbidden_words:
if word in value:
raise serializers.ValidationError(f"설명에 '{word}'(은)는 사용할 수 없습니다")
return value
# ============ 다중 필드 교차 검증 ============
def validate(self, attrs):
"""
모든 단일 필드 검증 완료 후 실행
필드 간 의존성 검증에 활용
"""
if attrs.get('is_execute') and not attrs.get('leader'):
raise serializers.ValidationError({
'is_execute': '프로젝트 실행 시 담당자는 필수입니다'
})
return attrs
핵심 필드 파라미터 정리
| 파라미터 |
역할 |
주의사항 |
read_only=True |
응답에만 포함, 입력 무시 |
required와 동시 사용 시 read_only 우선 |
write_only=True |
요청에만 포함, 응답 제외 |
비밀번호 등 민감 정보에 활용 |
required=False |
선택 입력 필드로 설정 |
default 미설정 시 None 할당 가능 |
default=<value> |
미입력 시 기본값 적용 |
required=True와 동시 사용 불가 |
allow_null=True |
null 값 허용 |
CharField 등에서 None vs "" 구분 |
allow_blank=True |
빈 문자열 허용 |
CharField, TextField 전용 |
trim_whitespace=True |
좌우 공백 자동 제거 |
기본값 True, CharField 적용 |
검증 우선순위 및 흐름
입력 데이터: {"name": "플랫폼 2024", "leader": " 홍길동 ", "desc": "테스트", "is_execute": true}
[1단계: 필드 타입 검증]
→ CharField, BooleanField 등 선언 타입과 일치 여부 확인
[2단계: 필드 수준 validators 실행]
→ max_length, min_length 등 내장 규칙
→ validators=[...]에 등록된 커스텀 함수들
[3단계: 단일 필드 validate_<field> 메서드]
→ def validate_name(self, value): ...
→ def validate_leader(self, value): ...
[4단계: 객체 수준 validate 메서드]
→ def validate(self, attrs): ...
→ 여러 필드 간 논리 검증
[5단계: 검증 결과 접근]
serializer.is_valid() # True 시
→ serializer.validated_data # 검증 통과 데이터 (dict)
→ serializer.errors # 검증 실패 시 오류 정보
save() 메서드 활용한 간결 패턴
class ProjectManageView(View):
def post(self, request):
payload = json.loads(request.body.decode('utf-8'))
# create/update 통합 처리
serializer = ProjectSerializer(data=payload)
serializer.is_valid(raise_exception=True)
# ModelSerializer 미사용 시 create() 메서드 오버라이드 필요
instance = serializer.save() # 내부적으로 create() 또는 update() 호출
# 저장된 객체 즉시 직렬화
return JsonResponse(
ProjectSerializer(instance=instance).data,
status=201,
json_dumps_params={'ensure_ascii': False}
)
Serializer 내부 create/update 커스텀
class ProjectSerializer(serializers.Serializer):
# ... 필드 정의 ...
def create(self, validated_data):
"""
.save() 호출 시 새 객체 생성 로직
ModelSerializer 자동 구현 vs Serializer 수동 정의
"""
# validated_data.pop('is_execute') # write_only 필드 제외 등 커스텀 가능
return Project_t.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
기존 객체 수정 로직
"""
instance.name = validated_data.get('name', instance.name)
instance.leader = validated_data.get('leader', instance.leader)
instance.desc = validated_data.get('desc', instance.desc)
instance.save()
return instance