장고 - Rest Framework 사용

  • 이번 포스트에서는 프론트엔드 개발자와 백엔드 개발자 간 협업을 위해 사용되는 REST API의 정의와 API를 생성하기 위한 Rest Framework 사용 방법에 대해 알아볼 것이다.

REST API란?

  • REST(Representational State Transfer)
    • 웹에 존재하는 모든 자원(이미지, 동영상, DB 자원)에 고유한 URI를 부여해 활용하는 것
    • 자원(Resource) - URI
    • 행위(Verb) - HTTP METHOD
    • 표현(Representations)
  • API(Application Programming Interface)
    • 컴퓨터 프로그램 간 상호작용을 촉진하며, 서로 정보를 교환할 수 있도록 도와주는 것
  • REST API
    • 웹에 존재하는 모든 자원에 고유한 URI를 부여해 활용 가능한 API를 제공하는 것
    • 백엔드 개발자와 프론트엔드, iOS, Android 개발자와의 협업을 위해 데이터 정보 교환이 가능하다.

Django Rest Framework 사용 방법

ex) dstagram_project

1. rest framework와 Swagger 설치

  • django-rest-framework 설치
    • $ pip install djangorestframework
  • Swagger 설치
    • $ pip install django-rest-swagger==2.1.2

2. INSTALLED_APP에 REST API 관련 기능 추가

  • 경로 : config(프로젝트) > settings.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    INSTALLED_APPS = [
    ...
    'rest_framework',
    # swagger UI 제공
    'rest_framework_swagger',
    # 토큰 제공
    'rest_framework.authtoken',
    # 특정 필드 서치 기능 제공
    'django_filters',
    ]

3. API 동작을 위한 Serializers 생성

  • 경로 : photo > serializers.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from rest_framework import serializers

    from .models import Photo

    class PhotoListSerializers(serializers.ModelSerializer):
    class Meta:
    model = Photo
    fields = '__all__'

    class PhotoSerializers(serializers.ModelSerializer):
    class Meta:
    model = Photo
    fields = '__all__'

4. 인증 권한 부여 방법 설정

  • 경로 : photo > permissions.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from rest_framework import permissions

    # 요청한 클라이언트가 해당 객체(포스트)의 생성자인 경우 인증 권한 부여
    class IsOwnerOnly(permissions.BasePermission):
    def has_object_permmission(self, request, view, obj):
    return obj.author == request.user

    # 요청한 클라이언트가 해당 객체(포스트)의 생성자이거나, 관리자인 경우 인증 권한 부여
    class IsOwnerAndAdminOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
    return obj.author == request.user or request.user.is_superuser

    # 모두에게 읽기 권한 부여하, 요청한 클라이언트가 해당 객체(포스트)의 생성자이거나, 관리자인 경우 인증 권한 부여
    class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
    # 읽기 권한은 모두에게 허용
    # GET, HEAD, OPTIONS 요청은 항상 허용
    if request.method in permissions.SAFE_METHODS:
    return True
    # 쓰기 권한은 해당 객체의 소유자 혹은 관리장에게만 부여
    return obj.author == request.user or request.user.is_superuser

5. settings.py에 기본 인증 방식, 토큰 방식, 서치 기능 추가

  • 경로 : config > settings.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    REST_FRAMEWORK = {
    # 로그인해야 모든 API 실행 가능
    'DEFAULT_PERMISSION_CLASSES': (
    'rest_framework.permissions.IsAuthenticated',
    ),
    # 토큰 인증 방식 추가
    'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework.authentication.TokenAuthentication',
    ),
    # 서치 기능 추가
    'DEFAULT_FILTER_BACKENDS': (
    # 필터 가능
    'django_filters.rest_framework.DjangoFilterBackend',
    # 검색 필드 적용하여 ForeignKey로 묶인 필드들을 기준으로 검색 가능
    # SearchField를 필터 백엔드에 추가
    'rest_framework.filters.SearchFilter',
    ),
    }
    # Swagger에 Token 인증 방법 추가
    SWAGGER_SETTINGS = {
    'SECURITY_DEFINITIONS': {
    "api_key": {
    "type":"apiKey",
    "name":"Authorization",
    "in":"header",
    }
    }
    }

6. generic 이용하여 뷰 작성

  • 경로 : photo > views.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from rest_framework import generics
    from .serializers import *
    from .permissions import *

    # list, create 기능을 하나의 클래스로 정의
    class PhotoListView(generics.ListCreateAPIView):
    queryset = Photo.objects.all()
    serializer_class = PhotoListSerializers

    # ForeignKey로 연결된 필드의 키워드 서치
    search_fields = ('text','author__username')

    # create 시, 요청한 클라이언트를 요청한 데이터의 author로 설정
    def create(self, request, *args, **kwargs):
    request.data['author'] = request.user.id
    return super().create(request, *args, **kwargs)

    # 기본적으로 모든 API 뷰가 로그인해야만 실행될 수 있도록 하는 IsAuthenticated 호출
    from rest_framework.permissions import IsAuthenticated

    # detail, update, delete 기능을 하나의 클래스로 정의
    class PhotoDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Photo.objects.all()
    serializer_class = PhotoSerializers

    # 로그인 이후 모든 API 뷰가 실행하도록 하고, 요청하는 클라이언트가 객체 생성자 혹은 관리자일 경우에 인증 권한 부여
    permission_classes = [IsAuthenticated, IsOwnerAndAdminOnly]

7. API 뷰의 경로 설정

  • 경로 : photo > urls.py

    1
    2
    3
    4
    5
    6
    7
    from django.urls import path

    urlpatterns = [
    ...
    path('api_list/', PhotoListView.as_view()),
    path('api_detail/<int:pk>/', PhotoDetailView.as_view()),
    ]
  • 경로 : config > urls.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from django.contrib import admin
    from django.urls import include, path
    from rest_framework_swagger.views import get_swagger_view
    from rest_framework.authtoken.views import obtain_auth_token


    schema_view = get_swagger_view(title='Dstagram API')

    urlpatterns = [
    ...
    # swagger UI 페이지 이동 위한 경로 설정
    path('api/doc/', schema_view),
    # 토큰 부여받기 위한 경로 설정
    path('api/get_token/', obtain_auth_token),
    ]

8. accounts 앱에 serializers.py 생성

  • 경로 : accounts > serializers.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    from rest_framework import serializers

    from django.contrib.auth import get_user_model

    User = get_user_model()

    # 유저 목록에 출력될 형식
    class UserListSerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ['id', 'username', 'first_name', 'last_name', 'email']

    # 회원 가입할 때 필요한 필드들에 관한 serializer
    class UserCreateSerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ['username', 'password', 'first_name', 'last_name', 'email']
    # 비밀번호 암호화(해쉬함수)하여 설정할 수 있도록 해주는 함수
    def create(self, validated_data):
    user = User.objects.create(**validated_data)
    user.set_password(validated_data.get('password'))
    user.save()
    # create view 는 항상 생성한 객체 반환
    return user

    class UserModifySerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ['password', 'first_name', 'last_name', 'email']

    def update(self, instance, validated_data):
    for key, value in validated_data.items():
    if value:
    setattr(instance, key, value)
    if key == 'password' and value:
    instance.set_password(value)
    elif value:
    setattr(instance, key, value)
    instance.save()

    return instance

    class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ['id', 'username', 'password', 'first_name', 'last_name', 'email', 'is_superuser']

9. serializer를 활용한 뷰 작성

  • 경로 : accounts > views.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    from .serializers import UserListSerializer, UserCreateSerializer, UserModifySerializer, UserDetailSerializer
    from rest_framework import generics
    from rest_framework.permissions import AllowAny

    class UserListAPI(generics.ListAPIView):
    queryset = get_user_model().objects.all()
    serializer_class = UserListSerializer
    # 원하는 검색 항목 추가
    filterset_fields = ('username', 'first_name')

    # 관리자의 경우에만 전체 목록 반환하고, 아닌 경우에는 자신의 정보만 반환
    def get_queryset(self):
    queryset = super().get_queryset()
    if not self.request.user.is_staff:
    queryset = queryset.filter(pk=self.request.user.id)
    return queryset

    # 인증 없이도 사용할 수 있도록 AllowAny 설정(회원가입 뷰 등)
    class UserCreateAPI(generics.CreateAPIView):
    serializer_class = UserCreateSerializer
    permission_classes = [AllowAny]

    class UserUpdateAPI(generics.UpdateAPIView):
    queryset = get_user_model().objects.all()
    serializer_class = UserModifySerializer

    class UserDetailAPI(generics.RetrieveAPIView):
    queryset = get_user_model().objects.all()
    serializer_class = UserDetailSerializer

    class UserDeleteAPI(generics.DestroyAPIView):
    queryset = get_user_model().objects.all()

10. 뷰에 대한 경로 설정

  • 경로 : accounts > urls.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from django.urls import path
    from .views import *

    app_name = "accounts"

    urlpatterns = [
    ...
    path('create/', UserCreateAPI.as_view()),
    path('list/', UserListAPI.as_view()),
    path('detail/<int:pk>/', UserUpdateAPI.as_view()),
    path('delete/<int:pk>/', UserDeleteAPI.as_view()),
    ]

추가) Swagger UI 접속하여 데이터 정보 확인(DEBUG 모드일 경우)

  • 127.0.0.1:8000/api/doc 접속
  • api > /api/get_token/
    • username, password 입력하여 token 확인
  • 우측 상단 ‘Authorize’ 버튼 클릭
    • value 값에 ‘Token [token 번호]’ 입력
  • 필요한 데이터 확인

추가) postman 프로그램으로 데이터 정보 확인 (DEBUG 모드일 경우)

  • postman 프로그램 이용하여 회원 정보 저장 및 데이터 확인
    • token 받아오기
      • 메인 화면에서 POST 선택 > 127.0.0.1:8000/api/get_token/ 입력 > Body 선택
      • KEY 값에 ‘username’, ‘password’ 입력하고, VALUE 값에 해당 회원정보 입력
      • SEND
      • token 번호 확인
    • 특정 데이터 정보 받아오기
      • Swagger에서 받아오고 싶은 데이터 파악
      • postman 실행
      • 메인화면에서 GET 선택 > 127.0.0.1:8000/accounts/list 입력 > Headers 선택
      • KEY 값에 ‘Authorization’ 입력하고, VALUE 값에 ‘Token [token 번호]’ 입력
      • SEND
      • 데이터 확인