본문 바로가기

프로그래밍/Django

[Django] Django Rest Framework(DRF) 알아보기 1부

REST_API

Django REST Framework (DRF)

REST

REST에 관해서 많은 블로그 글들이 자세하게 설명하고 있기에 여기서는 간단하게 설명하고자 한다.

REST는 WEB 관련 기술이다. 데이터를 주고받는 일종의 규약이다. 단순하다. 사용하기 편하다. Open API와 궁합이 좋다.

HTTP 기본 메서드 4가지(POST, GET, PUT, DELETE)를 통해 일관적인 컨벤션(관례)을 유지한다면 RESTful 하다고 한다.

좀 더 자세히 알아보고 싶다면 이 블로그를 추천한다.
http://blog.remotty.com/blog/2014/01/28/lets-study-rest/

직렬화 하기

처음에 이름부터 너무 어렵게 느껴져서, 직렬화라는 용어 자체가 두려움의 대상이었다. 그래서, 초심자를 위해 굉장히 쉽게 설명하고자 한다.
정확한 설명은 아니지만 처음 용어를 접하는 독자를 기준으로 설명하고자 한다.

직렬화의 필요성

직렬화를 왜 하냐고 물어본다면 다른 환경과 데이터를 주고 받으려면 동일한 데이터 구조를 가져야 하기 때문이다.

다른 환경이란 여러 주체들이 될 수 있다. 프로그램, 서버, 다른 컴퓨터, 핸드폰, TV 등 다양한 디바이스

제각기 다른 언어를 사용한다면 데이터 송신이 이루어질 수 없다.

따라서, 파이썬만 직렬화를 하는 것이 아니다. C, Java, PHP, R 여러 언어들이 직렬화를 지원한다.

시리얼라이즈는 마치 줄줄이 소시지처럼 통일된 데이터를 전송가능한 형태로(데이터 스트림) 만드는 것이라고 생각하면 된다.

물론, 반직렬화(역직렬화)도 있다. 여기서 반은 반대(反)를 의미한다. 절반(半)을 의미하는 것이 아니다. 영어로는 deserialization이다.

일련의 바이트로부터 데이터 구조를 추출하는 것이 반직렬화(deserialization)한다.

요약
파이썬 형식의 코드를 다른 네트워크 환경과 통신을 위해 코드를 직렬화(serialization)라고 한다.
파이썬에서 직렬화를 담당하는 클래스가 바로 Serializer이다.

예제

http://raccoonyy.github.io/drf3-tutorial-1/
글을 필요한 부분만 발췌해서 썼습니다. 가상환경 설정, 기본 환경세팅에 대해서는 위 글을 참조하셔야 합니다.

snippets/models.py (모델 생성)

class Snippet(models.Model):  
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
 
    class Meta:
        ordering = ('created',)

makemigrationsmigrate를 진행한 뒤, Snippets인스턴스를 생성하면서 DB에 저장하는 작업까지 진행한다.

from snippets.models import Snippet
 
snippet = Snippet(code='foo="bar" \n')
snippet.save()
snippet = Snippet(code="""print "hello world" """)
snippet.save()

그러면 DB에는 아래와 같이 저장된다.

스크린샷, 2017-07-11 22-17-32

아래 예제에서는 DB에 저장했던 인스턴스를 직렬화하는 예제이다.

from snippets.serializers import SnippetSerializer
 
serializer = SnippetSerializer(snippet)
serializer.data # ReturnDict 형 객체를 반환한다. 
 
ReturnDict([('pk'2), 
            ('title'''), 
            ('code''print "hello, world" \n'), 
            ('linenos'False), 
            ('language''python'), 
            ('style''friendly')])

snippet을 시리얼라이즈 클래스에 담은 다음에, data를 뽑아보면 딕셔너리 형태를 돌려준다. 이를 JSON 형식으로 바꿔주는 작업을 진행한다.

from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
 
content = JSONRenderer().render(serializer.data)
content
 
b'{"pk":2,"title":"","code":"print \\"hello, world\\\\n","linenos":false,"language":"python","style":"friendly"}'

나도 착각했지만, JSONRenderer를 했다고 해서 타입이 JSON이 나오는게 아니다. 문자열 객체인 bytes 가 나온다. json은 사람이 읽기 편하게 만든 문자열 포맷에 불과하다.

API VIEW 사용하기

http://raccoonyy.github.io/drf3-tutorial-2/
글을 필요한 부분만 발췌해서 썼습니다. def snippet_detail 함수는 본 포스팅에서 제외했습니다.

뷰(View)

우리가 뷰(view)를 작성한다고 하면 views.py라는 파일에 작성해왔다. 이 뷰(view)는 함수로도 작성할 수 있고, 클래스로 작성할수도 있다. 공통점은 요청(request)을 받아서 응답(repsonse)을 반환해주는 호출 가능한 객체라는 것이다.

요청(request)

REST Framework의 RequestHttpRequest를 확장하여 요청을 분석(파싱)한다.

reuest.data가 핵심이다.

쉽게 말해서 크롬에서 "www.naver.com에 접속할거야!"" 라고 하는 것이 요청(request)이다.

응답(response)

return Response(data) : 클라이언트에게 리턴할 컨텐츠 형태로 변환한다.

클라이언트에게 리턴할 컨텐츠 형태로 변환한다.

쉽게 말해서 요청이 오면 홈페이지를 띄워주는 것이 응답(response)이다.

API VIEW 를 사용해보자

  1. @api_view : 함수 기반 뷰에서 사용할 수 있다.

  2. APIView : 클래스 기반 뷰에서 사용할 수 있다.

이 래퍼(wrapper)들은 뷰(view)에서 받은
Request에 몇몇 기능을 더하거나
Response에 특정 context를 추가한다.

함수 기반 뷰 작성하기

@api_view(['GET', 'POST'])
def snippet_list(request):
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippetsmany=True)
        return Response(serializer.data)
 
    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.datastatus=status.HTTP_201_CREATED)
        return Response(serializerstatus=status.HTTP_400_BAD_REQUEST)

가장 큰 변화는 세 가지다.

  1. 지금은 함수기반 뷰를 사용하기 때문에 데코레이터(@api_view)를 사용한다는 점.

  2. request.data를 사용한다는 점.

  3. return Response를 사용한다는 점.

    • 응답코드도 좀 더 명시적으로 사용이 가능하다.

클래스 기반 뷰 작성하기

http://raccoonyy.github.io/drf3-tutorial-3/
위 튜토리얼을 진행한 뒤, 필요한 부분만 발췌해서 사용했습니다.

클래스 형 뷰의 장점

  1. 클래스형 뷰는 IF문이 없어 깔끔한 코드 생산이 가능하다.

  2. 다중 상속 등 객체지향기술(OOP)을 통한 코드 재사용성 UP'

    • 다중상속은 믹스인(Mixin) 클래스를 통해 이루어진다.

    • 믹스인을 사용해 클래스를 레고처럼 이리저리 조합할 수 있다.

class SnippetList(APIView):
    """
    코드 조각을 모두 보여주거나 새 코드 조각을 만듭니다.
    """
 
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippetsmany=True)
        return Response(serializer.data)
 
    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.datastatus=status.HTTP_201_CREATED)
        return Response(serializer.errorsstatus=status.HTTP_400_BAD_REQUEST)
  1. 함수 뷰와는 다르게 클래스 뷰에서는 데코레이터가 없는 대신, APIView를 상속받는다.

  2. urls.py를 적절하게 수정해야 한다.

    url(r'^snippets/$'views.SnippetList.as_view()),
    • urls.py는 클래스가 아니라 함수에 요청을 전달한다.

    • 따라서, 클래스를 사용하고자 하면 as_view()를 사용한다.

      • as_view()는 클래스의 인스턴스를 생성하는 역할을 한다.

      • 동시에 dispatch() 메소드까지 호출한다.

    • dispatch() 메소드는 요청(request)을 검사한다.

      • 해당 이름을 갖는 메소드로 요청을 중개한다.

      • 해당 메소드가 정의되어 있지 않으면, HttpResponseNotAllowd 예외를 발생시킨다

믹스인 사용하기

  1. 클래스형 뷰는 IF문이 없어 깔끔한 코드 생산이 가능하다.

  2. 다중 상속 등 객체지향기술(OOP)을 통한 코드 재사용성 UP'

    • 다중상속은 믹스인(Mixin) 클래스를 통해 이루어진다.

    • 레고처럼 이리저리 조합할 수 있다.

클래스형 뷰의 장점에서 조합에 대한 이야기를 했다. 믹스인(Mixin)의 말 그대로, 클래스에 기능들을 갈아 넣을수가 있다.

class SnippetList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    """
    코드 조각을 모두 보여주거나 새 코드 조각을 만듭니다.
    """
 
    queryset = Snippet.objects.all() # 쿼리셋 
    serializer_class = SnippetSerializer # 시리얼라이저 
 
    def get(self, request, *args, **kwargs):
        return self.list(request*args**kwargs)
 
    def post(self, request, *args, **kwargs):
        return self.create(request*args**kwargs)

믹스인 2개를 갈아 넣었다. 믹스인의 이름으로 유추해보면 하나는 리스트(목록)을 가져오는 것이고(GET 메서드), 하나는 무언가를 생성한다.(POST 메서드)

믹스인 만으로는 기존 APIView를 대체할 수 없기 때문에 GenericAPIView를 마지막에 추가했다. querysetserializer를 클래스 변수로 사용함으로써 그안에 메서드가 굉장히 압축적으로 표현된다.

Generic Class 기반 뷰 사용하기

이제 가장 강력한 제네릭 클래스를 만나볼 것이다. 짱짱맨이다. 코드가 굉장히 압축된다.

from snippets.models import Snippet  
from snippets.serializers import SnippetSerializer  
from rest_framework import generics
 
 
class SnippetList(generics.ListCreateAPIView):  
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

참고한 글

  1. http://raccoonyy.github.io/drf3-tutorial-1/
    http://raccoonyy.github.io/drf3-tutorial-2/
    http://raccoonyy.github.io/drf3-tutorial-3/
    본 포스팅은 위 글을 바탕으로 작성되었습니다.

  1. http://lunadev.tistory.com/12

    • 시리얼라이저(serializer)가 무엇인지 빠르게 살펴볼 수 있었습니다.

  2. http://ruaa.me/django-view/

    • as_view() 메서드 및 클래스 형 뷰의 장점에 대한 정보를 얻었습니다.