본문으로 건너뛰기
김신건의 로그

[DRF] Request + Response 객체

· 수정 · 📖 약 2분 · 532자/단어 #drf #request #response #api
DRF Request, DRF Response, request.data, request.query_params, Response.status_code, content_negotiation

정의

DRF 의 Request = Django HttpRequest확장. Response = TemplateResponse 의 확장. 자동 content negotiation + renderer 처리.

요청이 뷰에 도달하기까지

request.data 에 값이 실제로 들어가려면 그 이전에 여러 단계를 거친다.

Request 객체는 위 파이프라인에서 2번 (Router 이후) 에 만들어지고, 파이프라인 내내 확장된다:

단계Request 에 붙는 것
초기화request.data (파서 지연 로딩), request.query_params
Authenticationrequest.user, request.auth
Permission(검사만)
Throttle(검사만)
View여기서 처음 읽음
RendererResponse 를 실제 bytes 로

Request 객체

from rest_framework.decorators import api_view

@api_view(['POST'])
def create(request):
    request.data          # 파싱된 body (JSON, form 자동)
    request.query_params  # ?key=value
    request.user          # 인증된 User (or AnonymousUser)
    request.auth          # 토큰 등
    request.method        # 'GET', 'POST', ...
    request.META          # HTTP 헤더 등
    return Response(...)

request.data vs request.POST

flowchart LR
    Body["Request body"] --> Parser["DRF Parser (JSON, MultiPart, Form)"]
    Parser --> Data["request.data"]

    Django["Django request.POST"] --> Form["form data 만"]
request.data (DRF)request.POST (Django)
JSON body아니오
Form data
Multipart
PUT / PATCH body아니오

IMPORTANT

DRF 는 request.data 사용. request.POST 는 JSON body 를 못 읽는다.

Content Negotiation

sequenceDiagram
    Client->>DRF: POST /api/users/<br/>Content-Type: application/json
    DRF->>Parser: JSONParser 선택
    Parser-->>DRF: 파싱된 데이터
    DRF->>View: request.data = {...}
    View-->>DRF: Response({...})
    DRF->>Renderer: Accept: application/json<br/>→ JSONRenderer
    Renderer-->>Client: {...}

DRF 가 자동으로 Content-Type 보고 파싱 + Accept 보고 렌더링. 자세한 건 drf-versioning-content-negotiation.

Response 객체

from rest_framework.response import Response
from rest_framework import status

@api_view(['GET'])
def user_detail(request, pk):
    user = get_object_or_404(User, pk=pk)
    return Response(
        UserSerializer(user).data,
        status=status.HTTP_200_OK,
        headers={'X-Custom': 'value'},
    )
파라미터의미
data응답 데이터 (dict, list, 등)
statusHTTP status code
template_nameHTML template (Browsable API)
headers추가 헤더
content_type강제 지정 (일반적으로 안 함)

Status Codes

from rest_framework import status

# 자주 쓰는 것
status.HTTP_200_OK
status.HTTP_201_CREATED
status.HTTP_202_ACCEPTED
status.HTTP_204_NO_CONTENT

status.HTTP_400_BAD_REQUEST
status.HTTP_401_UNAUTHORIZED
status.HTTP_403_FORBIDDEN
status.HTTP_404_NOT_FOUND
status.HTTP_409_CONFLICT
status.HTTP_422_UNPROCESSABLE_ENTITY
status.HTTP_429_TOO_MANY_REQUESTS

status.HTTP_500_INTERNAL_SERVER_ERROR
status.HTTP_502_BAD_GATEWAY
status.HTTP_503_SERVICE_UNAVAILABLE

HTTP_200_OK 같은 상수 사용 권장 (magic number 회피).

Parser 종류

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',      # 기본
        'rest_framework.parsers.FormParser',       # multipart 이전
        'rest_framework.parsers.MultiPartParser',  # 파일
    ]
}
ParserContent-Type
JSONParserapplication/json
FormParserapplication/x-www-form-urlencoded
MultiPartParsermultipart/form-data
FileUploadParserbinary (파일 stream)

View 단위 override

from rest_framework.parsers import MultiPartParser, JSONParser

class FileUploadView(APIView):
    parser_classes = [MultiPartParser]

    def post(self, request):
        file = request.FILES['file']
        return Response({'uploaded': file.name})

Renderer 종류

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}
RendererAccept
JSONRendererapplication/json
BrowsableAPIRenderertext/html
AdminRendererHTML admin
TemplateHTMLRendererHTML 지정
HTMLFormRendererHTML form

Format Suffix

# URL: /users.json 또는 /users.api
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = format_suffix_patterns([
    path('users/', UserList.as_view()),
    path('users/<int:pk>/', UserDetail.as_view()),
])

URL 확장자로 format 지정. RESTful 이라기보다 옛 패턴, Accept 헤더 권장.

파일 업로드

class FileUploadView(APIView):
    parser_classes = [MultiPartParser, FormParser]

    def post(self, request):
        file = request.data.get('file')
        # 저장
        return Response({'name': file.name, 'size': file.size})

자세한 건 django-file-uploads.

Query Params vs Data

@api_view(['GET'])
def search(request):
    q = request.query_params.get('q', '')       # URL query
    page = int(request.query_params.get('page', 1))
    ...
    return Response(...)


@api_view(['POST'])
def create(request):
    title = request.data.get('title')            # body
    ...

Custom Parser / Renderer

from rest_framework import renderers

class CSVRenderer(renderers.BaseRenderer):
    media_type = 'text/csv'
    format = 'csv'

    def render(self, data, accepted_media_type=None, renderer_context=None):
        import csv, io
        buf = io.StringIO()
        writer = csv.writer(buf)
        for row in data:
            writer.writerow(row.values())
        return buf.getvalue()


class MyView(APIView):
    renderer_classes = [renderers.JSONRenderer, CSVRenderer]

    def get(self, request):
        return Response([{'a': 1, 'b': 2}])
        # GET /myview.csv → CSV 응답

Exception 자동 처리

from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError

@api_view(['GET'])
def item(request, pk):
    try:
        item = Item.objects.get(pk=pk)
    except Item.DoesNotExist:
        raise NotFound('Item not found')

    if not item.can_view(request.user):
        raise PermissionDenied()

    return Response(ItemSerializer(item).data)

DRF 가 자동으로 ProblemDetail 유사 JSON 응답:

{ "detail": "Item not found" }

HttpResponse vs Response

# ❌ Django HttpResponse
from django.http import JsonResponse
return JsonResponse({'ok': True})

# ✓ DRF Response
return Response({'ok': True})

DRF Response = content negotiation + renderer 자동. HTML/JSON 자유롭게 응답.

다른 프레임워크 비교

FrameworkBody 접근
DRFrequest.data
FastAPIPydantic model 자동
Flaskrequest.get_json()
Spring@RequestBody DTO
Expressreq.body (body-parser)
Railsparams (모든 것 통합)

흔한 함정

WARNING

  1. request.POST 사용 = JSON body 못 읽음. request.data.
  2. status_code 매직 넘버 400 = 가독성 낮음. status.HTTP_400_BAD_REQUEST.
  3. JsonResponse 사용 = content negotiation 무시. Response.
  4. request.datadict() 로 변환 = QueryDict/OrderedDict 를 그대로 두는 게 안전.

관련 위키

이 글의 용어 (6개)
[API Design] REST API: 원칙, 자원 모델링, HATEOAS, 버전 관리api-design
정의 REST (Representational State Transfer) 는 Roy Fielding 의 2000년 박사논문에서 정립된 분산 시스템 아키텍처 스타일. HTTP 의…
[Django] DRF Serializer 심화django
정의 DRF Serializer는 JSON ↔ Python ↔ Model의 변환과 검증을 담당한다. Django Form과 비슷한 인터페이스이나 API/JSON에 특화. 검증, …
[Django] File Uploads + Storage: FileField, ImageField, S3django
정의 Django 의 파일 업로드 + 저장 시스템. FileField/ImageField + Storage backend 로 로컬 / S3 / GCS 등 자유롭게. Model 필…
[DRF] Tutorial + Quickstart: 첫 API 서버 5분django
정의 Django REST Framework (DRF) 를 처음 사용 하는 사람을 위한 순차 시작 가이드. 5분 안에 동작하는 API 서버 완성. DRF 개요는 . Django …
[DRF] Versioning + Content Negotiationdjango
정의 Versioning = API 의 여러 버전 공존. Content Negotiation = 같은 endpoint 가 여러 형식 (JSON, XML, HTML) 응답. Ver…
[DRF] Views: APIView, GenericViews, Mixins, FBVdjango
정의 DRF View 4가지 스타일. 자유도 vs 자동화 트레이드오프. 4가지 스타일 | 스타일 | 코드량 | 자유도 | |---|---|---| | | 유사 Django FBV…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기