개발 노트

DRF&React로 REST API 체험기

j4ko 2020. 11. 18. 22:10
728x90
반응형

DRF? React?

DRF란 Django-RestFramwork를 가리킨다. 어떤 에피소드를 겪고 말로만 들었던 REST API를 실제로 테스트해보기 위해 Back-End는 Django를 Front-End는 React로 잡은 뒤 Django로 REST API를 구성하고 실제로 React에서 REST API 서버에 Request를 때려 CRUD가 가능한지를 테스트해보는 게 목표였다.

 

그래서 이번 카테고리에는 이것들을 이용해 어떻게 구성하고 테스트했는지 포스팅해 볼 계획이다.

 

REST & REST API

REST와 REST API에 대해 간략히 알아보자 (말 그대로 간. 략. 히)

REST란 무엇일까? REST는 HTTP 기반으로 필요한 자원에 접근하는 방식이다. 여기서 자원이라 하면 저장된 데이터 즉 웹에서 클라이언트에게 제공하는 이미지/동영상/문서/파일 같은 것들을 포함하는 말이다. REST의 제약조건을 살펴보자.

  1. Client-Server (클라이언트/서버)
    1. :  클라이언트와 서버로 역할을 구분한다. 이것은 CS 구조에서 크게 벗어나지 않는다.
    2. : 서버는 API를 제공, 클라이언트가 API 요청 시 로직 처리와 데이터 저장을 책임진다.
    3. : 클라이언트는 사용자 인증, 상태(세션 로그인 정보) 관리와 서버 리소스 요청을 책임지는 구조로 역할을 구분한다.
  2.  Stateless (무상태)
    1. : REST 서버는 작업을 위한 상태 정보 (Cookie, Session)을 관리하지 않아야 함
    2. : 서버와 클라이언트가 독립되어 있어 REST 요청 하나에 필요한 모든 정보를 담고 있지 않으면 데이터를 해석할 수 없다.
  3. Cacheable (캐시)
    1. : HTTP가 가진 캐싱 기능이 적용되어야 함
  4. Layered System (계층화)
    1. : 서버를 다중 계층으로 구성 가능
    2. : 로직을 수행하는 API 서버와 그 앞단에 사용자 인증, 암호화, 로드밸런싱 등의 계층을 추가해 구조상의 유연상 제공
  5. Uniform Interface (인터페이스 일관성)
    1. : 아키텍처를 단순화하고 분리한 뒤 각 부분을 독립적으로 발전 

REST API란 무엇일까? REST 기반으로 서비스 API를 구현한 것을 REST API라 한다.

 

 

Django의 "Project"와 "app"

Django의 구조를 생각해보자 아마 django로 프로젝트를 만들기 위해 다음과 같은 명령어를 쓸 것이다.

# Django INSTALLATION
$ pip install django

# Django Start Project
$ django-admin startproject myproj

pip로 Django를 설치하고 django-admin 명령어로 프로젝트를 생성하게 된다. Django를 한 번 이용해본 사람이라면 이렇게 생성된 "myproj"라는 폴더에서 기능을 추가하고 관리하는 것을 알 수 있을 것이다. 다음의 경우를 봐보자

$ python manage.py startapp myapp

위의 명령어는 "myproj"라는 프로젝트에 "app"을 추가하는 명령어이다. 즉 추가하고 싶은 특정 기능이 있을 때 위의 명령어로 app을 추가하여 사용하는 형태라고 생각할 수 있다. 즉 다음과 같이 생성하면 REST API 서버를 만들기 위한 프로젝트가 생성되는 것이다.

# REST API 프로젝트 생성
$ django-admin startproject myrest

# 생성된 프로젝트의 경로로 이동한 뒤

# REST API APP 추가
$ python manage.py startapp restapp

내 경우엔 다음과 같이 구성했다.

# 프로젝트 명은 "rest-server"
$ django-admin startproject rest-server

# app 이름은 "datalab"
$ python manage.py startapp datalab

 

 

 

DRF INSTALLATION

계속해서 Django에서는 rest-framework 패키지를 설치해줘야 한다.

$ pip install django-restframework

그리고 생성된 프로젝트의 setting.py에 다음과 같은 구문을 추가해준다.

[Setting.py]
# Add
INSTALLED_APPS = [
        ...,
        'datalab' # app 
        'rest-framework', # rest-fraemwork 
]

# REST FRAMEWORK SETUP
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10
}

INSTALLED_APPS의 끝 부분에 생성된 app인 "datalab"을 추가하고 그 아래로 설치한 rest-framework를 추가해주었다. 그 밑의 REST_FRAMEWORK는 일단 추가하고 나중에 이 부분을 이용할 때 자세히 보기로 했다.

 

 

REST API를 다루기 위한 장고 셋팅

지난번 글(jakpentest.tistory.com/66)까지 해서 DRF를 설치하고 settings.py까지 설정을 한 뒤 app 생성까지 마치게 됐다. 이번 포스팅은 "모델"이란 것을 생성하고 Django REST API 페이지까지 진입하는 것을 목표로 해보자

DRF를 다루기 위한 것이 목적이니 MVT가 무엇인지에 대한 것은 생략하고 바로 설정하는 방법으로 들어가 보자. 먼저 Model 부분이다

# datalab/models.py

from django.db import models

# Create your models here.

class retDataclasu(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    label = models.CharField(max_length=100)
    update_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.label

데이터가 생성된 시간과 사용자 입력 값으로 받는 "labal" 그리고 추가로 업데이트 시간을 체크하려고 update_at 모델을 정의했다. 변수의 우변에 보면 models 객체에서 필드를 받아서 선언되는 것을 알 수 있다. 즉 사용하려는 데이터 구조 DB의 "레코드"를 생성한 것으로 생각해 볼 수 있다.

# datalab/serializer.py
from .models import retDataclasu
from rest_framework import serializers

class dataSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = retDataclasu
        fields = '__all__'

위의 코드는 app에서 새로 생성해줘야 하는 파일이다. 여기서 조금 어려운 개념이 등장하는데 바로 "Serializer"(시리얼라이저)이다. 파일 이름도 serializer.py인 것을 알 수 있다. DRF에서 쓰이는 Serializer의 개념은 models.py에서 생성한 레코드들을 REST 형식(Ex, {"key":"value"})으로 바꿔주는 것이라고 한다. 다음은 views.py이다

# datalab/views.py
from .models import retDataclasu

# viewsets
from rest_framework import viewsets

# 같은 경로에 존재하는 serializer.py의 model을 import
from .serializer import dataSerializer

# viewsets.ModelViewSet으로 view가 가능
class dataViewSet(viewsets.ModelViewSet):
	# queryset : serializer에서 생성한 class의 모든 객체를 가져온다
    queryset = retDataclasu.objects.all()
    serializer_class = dataSerializer

위의 views.py는 urls.py에 다음과 같이 등록해주자.

# /urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include

# routers import
from rest_framework import routers
# datalab의 views를 별칭으로 "dviews"로 
from datalab import views as dviews

# router 객체를 생성
router = routers.DefaultRouter()
# data로 접근하는 http 요청은 datalab의 views.py를 바라보게 될 것
router.register(r"data", dviews.dataViewSet)

urlpatterns = [
	# urlpatterns에 router 객체를 등록
    path('',include(router.urls)),
    url(r"^admin/", admin.site.urls),
]

 위의 urls.py는 처음 프로젝트를 생성했을 때 만들어진 urls.py 파일이다. 개인적으로 이 부분이 어려웠는데 구글을 통해 수집한 urls.py에 url 경로를 설정하는 방법이 매우 다양했다.  내 경우엔 위와 같이 routers를 이용해 등록하는 방법을 선택했다. 이제 테스트를 위한 준비는 끝났다. 

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver

위와 같이 migration을 해주고 서버를 실행 키고 아래의 주소로 접속해보자

http://127.0.0.1:8000/data

아래와 같은 화면이 나오면서 접속이 되는가? 그렇다면 성공이고 만약 실패했다면 다시 체크해보고 구글을 통해 정보를 모은 뒤 다시 도전해보자.. 만약 아래 이미지 같이 데이터가 없더라도 굳이 신경쓰진 말자 필자가 테스트용으로 넣어둔 데이터이다. 중요한 것은 아래와 같은 "Django Rest framwork"라는 페이지가 나오는 지에 초점을 맞추자

 

 

CORS 정책

Django의 REST API 테스트 환경을 구축하고 이제 React에서 REST API를 호출해보고자 마음먹었는데 다음과 같은 에러가 일어나면서 커넥션이 일어나지 않았습니다. 왜일까요?

영어 지문처럼 이 긴 경고문은 CORS 정책에 위반되어 REST 서버에서 데이터를 가져오고 있지 못한다고 이야기합니다. 그렇다면 CORS란?

 

"CORS, Cross-Origin Resource Sharing 한국말로 '교차 출처 리소스 공유' 교차 출처라는 것은 출처가 다르다는 의미입니다.  즉 위의 에러 문은 다른 출처의 자원을 요청할 수 없다 라는 뜻이 됩니다." 

 

어 근데 뭔가 이상합니다. 자신의 로컬 PC에서 테스트하고 있는 환경인데 출처가 다르다고 하니 이게 무슨 뚱딴지같은 상황일까 하는 생각이 듭니다. 찾아본 결과 같은 환경에 존재하고 있는 서버도 포트가 다르면 다른 출처라고 인식되어 CORS 정책을 위반한다고 합니다. 즉 에러를 해결하기 위해선 BackEnd로 사용하고 있는 서버의 환경마다 CORS 설정을 허용해 주어야 합니다.

 
 

CORS 정책 설정 제대로 들어갔는데 왜?

"구글을 통해 django 서버에 CORS 설정을 제대로 했는데도 불구하고 React에서 Django의 Rest 서버에 데이터를 호출하려니 또 CORS가 위반되었다고 합니다. 왜 그럴까요?"

이건 딱히 구글을 통해서도 명확한 해결 방법이 나와있진 않았고 제가 해결한 방법은 React에서 Django에 Request를 시도할 위치가 다르다는 점이었습니다. 처음 Request를 할 때 REST API 페이지에 접속했고 DRF에서 제공해주는 데이터를 조회할 수 있는 페이지의 URL에 Request를 날렸습니다. 밑의 이미지는 URL이 "http://127.0.0.1:8000/data/1/" 과 입니다.

React에서 계속 이 URL에 Request를 했는데도 Response가 되지 않자 데이터 포맷을 달리해줘야 되지 않을까 하는 생각이 들었습니다. 제 경험상으로는 REST API는 json으로 결과를 return 받습니다.

 Content-Type이 "application/json"인 놈한테 Request&Reponse를 해서 응답 결과를 받아야 한다는 것입니다. 이 부분을 인지한 후 아래의 URL로 Request를 시도해봤더니 Response가 잘 이루어졌습니다. 이걸 모르고 Content-Type이 다르게 생긴 놈한테 삽질만 한 셈이었습니다. 

위의 URI는 Django에서 찾을 수 있었습니다.

 

 

728x90
반응형