페이지

2018년 7월 25일 수요일

1. 장고를 이용한 레스트풀 API 개발

- 간단한 SQLite 데이터베이스와 대화하는 레스트풀 API 디자인
- 각 HTTP 메서드가 수행하는 작업 이해
- 경량 가상 환경에서의 작업
- 장고 레스트 프레임워크에서의 가상 환경 설정
- 모델 제작
- 직렬화와 역직렬화 관리
- API 뷰 작성
- API에 대항 HTTP 요청
- 명령 행 도구로 작업 - curl과 httpie
- GUI 도구로 작업 -Postman과 기타
- 지식 테스트

1.1 간단한 SQLite 데이터베이스와 대화하는 레스트풀 API 디자인

먼저 주요 자원에 대한 요구사항을 지정(속성, 필드)
- 정수 식별자
- 이름 또는 타이틀
- 출시일
- 3D RPG 또는 2D 모바일 아케이드와 같은 게임 카테고리 디스크립션
- 플레이어가 적어도 한 번 게임을 했는지 여부를 나타내는 bool 값

다음 표에서는 첫 번째 API 버전에서 지원해야 하는 HTTP 동사verb, 범위, 그 메서드에 대한 의미를 보여준다. 각 메서드는 HTTP 동사와 범위로 구성되며, 모든 메서드는 모든 게임 및 컬렉션에 대해 잘 정의된 의미를 갖는다.

HTTP 동사           범위                   의미
GET           게임 켈렉션         겔렉션의 모든 저장된 게임을 얻고, 이름에 따라 오름차
                                       순으로 정렬한다.
GET           게임                  게임 하나만 얻는다.
POST         게임 켈렉션         케렉션에 새 케임 생성
PUT           게임                  기존 게임 업데이트
DELETE       게임                  기존 게임 삭제

레스트풀 API에서 각 자원은 고유한 URL를 가진다. 그러므로 우리 API에는 각 게임마다 고유한 URL이 있게 된다.


1.2 각 HTTP 메서드가 수행하는 작업 이해

http://localhost:8000/games/가 게임 겔렉션을 위한 URL이라고 생각해 보자.
http://localhost:8000/games/12/ 는 id또는 기본 키가 12인 게임을 식별한다.

새 게임을 만들려면 저 아래의 HTTP 동사(POST)와 요청 URL(http://hocalhost:8000/games/)로 HTTP요청을 작성해 보내야 한다. 더욱이 JSON(JavaScript Object Notation) 키-값쌍으로 필드 이름과 값을 제공해 새 게임을 만들어야 한다. 요청 결과, 서버는 필드에 제공된 값의 유효성을 검사하고 유효한 게임인지 확인 후 데이터베이스에 저장한다.

서버는 적절한 데이블에 새 게임이 들어간 새 행을 삽입하고, JSON으로 직렬화된 최근 추가 게임의 JSON본문과 201created상태 코드를 반환하는데, 여기에는 데이터 베이스가 자동으로 생성하고 게임 책체에 지정한 할당 id또는 기본 키가 포함된다.
POST http://localhost:8000/games/

id 또는 기본키가 지정된 숫자 값과 일치하는 게임을 얻으려면 HTTP 동사(GET)와 요청 URL(http://localhost:8000/games/{id}/)을 사용해야 저 아래의 HTTP 요청을 작성해 보내야 하는데, 여기서 {id}에는 id나 기본키에 해당하는 숫자 값을 지정한다.

예를들어, 요청 URL인 http://localhost:u8000/games/50/을 사용하면, 서버는 id 또는 기본 키가 50과 일치하는 게임을 얻는다.

요청 결과, 서버는 데이터베이트에 지정된 id또는 기본 키를 가진 게임을 얻고 파이썬에서 적절한 게임 개체를 생성할 것이다. 게임이 발견되면 서버는 게임 객체를 JSON으로 직렬화하고, 200OK 상태 코드와 함께 직렬화된 게임 객체가 들어간 JSON 본문을 반환한다. 지정된 id또는 기본 키와 일치하는 게이미 없으면 서버는 404 not Found 상태를 반환한다.
GET http://localhost:8000/games/{id}/

id 또는 기본 키가 지정된 숫자 값과 일치하는 게임을 업데이트하려면 HTTP 동사 (PUT)와 요청 URL(http://localhost:8000/games/{id}/)을 사용해 저 아래의 HTTP 요청을 작성해 보내야 한느데, 여기서 {id} 자리에는 제공되 ㄴ데이터로 생성된 게임의 해당 값으로 대체한다. 게다가 JSON 키-값 싸으로 필드 이름과 값을 제공해 기존 게임을 대체할 새 게임을 만들어야 한다. 요청 결과, 서버는 필드에 제공된 값의 유효성을 검사하고 유효한 게임인지 확인한 다음, 데이터베이스에서 지정된ID 또는 기본키와 일치하는 값을 새 값으로 바꾼다. 게임의 id또는 기본 키는 업데이트 작업 후에도 동일할 것이다. 서버는 새당 테이블의 기존 행을 업데이트하고, 200 ok 상태 곹드와 함께 최근에 업데이트 된 게임을 JSON으로 직렬화한 JSON본문을 반환한다. 새로운 게임에 필요한 모든 데이터를 제공하지 않으면 서버에서 400 Bad Request 상태코드를 반환 할 것이다. 서버가 지정된 ID로 게임을 찾지 못하면 404 Not Found 상태를 반환한다.
PUT http://localhost:8000/games/{id}/
id 또는 기본 키가 지정된 숫자 값과 일치하는 게임을 제거하려면 HTTP 동사(DELETE)와 요청 URL(http://localhost:8000/games/{id}/)을 사용해서 저 아래의 HTTP 요청을 작성해 보내야 하는데, 여기서 {id} 자리에 해당 숫자를 적는다. 예를 들어, 요청 URL로 http://localhost:8000/games/20/을 사용하면 서버는 id 또는 기본키가 20과 일치하느 게임을 삭제한다. 요청 결과, 서버는 데이터베이스에서 해야 id 또는 기본키를 가진 게임을 얻어 파이썬으로 적절한 게임 객체를 만든다. 게임이 발견되면 서브는 ORM에 요청해 이 게임 객체와 관련된 게임 행을 삭제하고 204 No Content 상태 코드를 반환한다. 지정된 id또는 기본 키와 일치하느 게임이 없으면 서버는 404 Not Found상태만 반환할 것이다.
DELETE http://locahost:8000/games/{id}

1.3 경량 가상 환경에서의 작업
파이썬에 통합된 venv 모듈로 생성된 가상 환경을 활성화하려면 자체에 설명해놓은 단계를 따르지 말고 필요한 경우에 적절한 메커니즘을 사용해 가상 환경을 활성화해야한다. venv 모듈은 도입한 PEP 405 파이썬 가상 환경에 대한 자세한 정보는 https://www.python.org/dev/peps/pep-0405/에서 볼 수 있다.

vens로 생성한 각 가상환경은 결리된 환경이며 사이트 디렉토리에 자체 파이썬 패키지가 독립적으로 설치된다. 파이썬 3.4 이상 버전에서 venv로 가상 환경을 만들면 pip가 새로운 가상환경에 포함된다. 파이썬 3.3에서는 가상 환경을 만든 후에 pip를 수동으로 설치애야 했다. 제공한 지지사항은 파이썬 3.5.x를 포함해 파이썬 3.4 이상과 호환된다는 점에 주목하라. 이 후의 명령들은 맥 os, 리눅스, 원도우에 파이썬 3.5.x가 설치돼 있다고 가정한 것이다.

먼저, 가상 환경을 위한 대상 폴더 또는 디렉터리를 선택해야 한다. 저 아래에 있는 것은 맥 os 와 리눅스용 예제에서 사용할 경로다. 가상 환경의 대상 폴더는 홈 디렉터리 내의 PythonREST/Django 폴더다. 예를 들어, 맥 os또는 리눅스의 홈 디렉터리가 /Users/gsaton이라면 /Users/gaston/PythonREST/Django에 가상 환경이 만들어진다.
각 명령에서 지정한 경로는 여러분이 원하는 경로로 바꿀 수 있다.

~/PythonREST/Django

다음은 원도우용 예제에서 사용할 경로다. 가상 환경의 대상 폴더는 사용자 프로필 폴더 내의 PythonREST/Django 폴더다. 예를 들어, 사용자 프로필 폴더가 C:\Users\Gaston이라면, 가상 환경은 C:\Users\gaston\PytonbREST\Django 내에 만들어진다. 각 명령에서 지정한 경로는 여러분이 원하는 경로로 바꿀 수 있다.

%USERPROFILE%\PythonREST\Django

이제 -m옵션 뒤에 venv모듈 이름과 원하는 경로를 사용해 파이썬이 이 모듈을 스크립트로 실행해서 지정된 경롤에 가상환경을 만들게 한다. 이 지시사항은 가상환경을 만들 플랫폼에 따라 다르다.

맥OS 또는 리눅스에 터미널을 열고, 다름 명령을 실행해 가상 환경을 만든다.

python3 -m venv ~/PytjhonREST/Django01

원도우에서는 다음 명령을 실행해 가상 환경을 만든다.

python -m venv %USERPROFILE%\PythonREST\Django01

이 명령은 아무런 출려도 내지 않는다. 이 스크립트는 --without-pip 옵션을 지정하지 않았으므로 ensurepip를 호출해 지정된 대상 폴더를 생성하면서 pip를 설치한다. 지정된 대상 폴더에는 파이썬 실행 파일과 가상환경임을 나타내는 가가 파일이 포함된 새로운 디렉터리 트리가 생긴다.

pyenv.cfg 구성 파일로 가상환경에 대한 다양한 봅션을 지정하는데, 이 파일에 존재한다느느 것은 가상환겨으이 루트 폴더라는 것을 나타내는 지표가 된다. 맥 os와 리눅스에서의 루트 폴더에는 bin, include, lib, lib\python3.5, lib\python3.5\sitepackages와 같은 주요 서브 폴더가 있다. 원도우에서의 이 루트 폴더에는 Include, Lib, Lib]site-packages, Scripts와 같은 주요 서브 폴더가 있다. 각 프랫폼의 가상 환경에 대한 디렉토리 트리는 이 플랫폼에서의 파이썬 설치 레이아웃과 동일하다. 다음 스크린샷은 맥OS의 Django01가상 환경에 대해 생성된 디렉터리 트리의 폴더와 파일을 보여준다.

다음 스크린샷은 원도우의 가상 환경에 대해 생성된 디렉토리 트리의 메인 폴더를 보여준다.

가상환경을 활성화한 후, 타사 패키지를 가상 환경에 설치하면 모듈은 플랫폼에 따라 lib/python3.5\site-packages 또는 Lib\site-packages 폴더에 배치될 것이다. 실행 파일은 플랫롬에 따라 bin 또는 Scripts 폴더에 복사된다. 이렇게 설치한 패키지는 다른 가상 환경 또는 기본 파이썬 환경을 변경시키지 않는다.

가상 환경을 만들었으므로 이제 해당 플랫폼에 따른 스크립트를 실행해 가상 환경을 활성화 하낟. 가상 환경을 활성화 한 후에는 이 가상 환경에서만 사용할 수 있는 패키지를 설치할 것이다.

맥 os 또는 리눅스의 터미널에서 다음 명령을 실행하라. 터미널 세션에서 기본 셀이 아닌 다른 셀을 실행한 것이 아니라면, 이 명령의 결과가 정확하게 나올 ㅓㄳ이라는 점에 주목한다. 의심이 가는 경우에는 터미널 구성과 기본 설정을 점검하라.

echo $SHELL

이 명령은 터미널에서 사용중인 셀 이름을 표시한다. 맥 OS에 기본 값은 /bin/bash이며, 이는 bash셀로 작업하고 있음을 의미한다. 셀에 따라 맥os또는리눅스에서 다른 명령을 실행해 가상 환경을 활성화 해야 한다.

맥OS또는 리눅스에서 bash셀을 사용하게 터미널을 구성한 경우, 다음 명령을 실행해 가상 환경을 활성화 하라. 이 명령은 zsh셀에서도 작동한다.

source ~/PythonREST/Django01/bin/activate

터미널이 csh또는 tcsh셀을 사용하게 구성된 경우, 다음 명령을 실행해 가상 환경을 활성화 하라.

source ~/PythonREST/Django01/bin/activate.csh

터미널의 fish 셀을 사용하게 구성돼 있으면, 다음 명령을 실행해 가상 환경을 활성화 하라.

%USERPROFILE%\PythonREST\Django01\Scripts\activate.bat

Windows PowerShell을 선호한다면, 이를 실행하고 다음 명령을 실행해 가상 환경을 활성화하라. 하지만 스크립ㅌ느를 실행하려면 Windows PowerShell에서 스크립트 실행을 사용할 수 있도록 설정해야 한다는 점에 유의하라.

cd $env:USERPROFILE
pythonREST\Django01\Sripts\Activate.ps1

가상환경을 활성화하면, 명령 프롬프트에 가상 환경 루트 폴더 이름이 괄호로 묶여 기본 프롬프트의 접두어로 표시되므로 가상 환경에서 작업하고 있음을 알게 해준다. 여기서는 활성화된 가상 환경의 로트 폴더가 Django01이기 때문에 (Django01)이 명령 프롬프트의 접두어로 표시된다.

다음 스크린샷은 위에 나타낸 명령을 실행한 후, bash셀이 있는 맥os EL Capitan 터미널에서 활성화된 가상 환경을 보여준다.

위에 설명한 과정을 ㅗ생성된 가상 환경을 ㅣㅂ활성화하는 것은 매우 쉽다. 맥 os 또는 리눅스에서 deactivate를 입력하고 Enter를 누른다. 원도우에서는 명령프롬프트에서 Scripts폴더에 들어 있는 deativate.bat 배치 파일을 실행해야 한다. Windows PowerShell에서 Scripts 폴더에서 Deactivate.ps1스크립트를 실행해야 한다. 비 활성화하면 환경 변수에서 수행된 모든 변경사항이 제거될 것이다.

1.3 장고 레스트 프레임워크에서의 가상환경 설정
pip install django

pip install djangorestframework

cd ~/PythonREST/Django01

django-admin.py startproject gamesapi

cd gamesapi

paytho manage.py startapp games

occure "

Django manage.py runserver invalid syntax" error

Note that from exc is removed from the file. It is not required in the manage.py file.
(https://stackoverflow.com/questions/47880626/django-manage-py-runserver-invalid-syntax)


위의 명령으로 다음 파일들이 들어간 새 gamesapi/games서브 폴더가 생성됐다.

- __init__.py
- admin.py
- apps.py
- models.py
- tests.py
- views.py

gamesapi/games 폴더 내에 있는 apps.py 파일의 파이썬 코드를 확인해 보자. 

이 코드는 GamesConfig클래스를 장고 애플리케이션 및 해당 구성을 나타내는 django.apps.AppConfig 클래스의 서브 클래스를 선언한다. GamesConfig 클래스는 name 클래스 속성을 정의하면서 해당 값을 'games'로 설정한다. gamesapi 장고 프로젝트의 설정을 구성하는 gamesapi/settings.py 파일에는 설치 앱 중 하나로서 games.app.GamesConfig를 추가해야 한다. 이 문자열은 app-name+.apps.+클래스 이름으로 구성됐는데, 즉 games + .apps. + GamesConfig 식이 된다. 더욱이 장고 레스트 프레임워크를 사용할 수 있게 rest_framework앱도 추가해야 한다.

gamesapi/settings.py 팡리은 파시썬 모듈로서 gamesapi 프로젝트의 장고 구성을 정의하는 모듈 레벨 변수가 들어 있다. 우리는 이 장고 설정 파일을 약간 변경할 것 이ㅏㄷ.
gamesapi/settings.py 파일을 열고 설치된 앱 선언의 문자열 리스트를 지정하는 다음 행을 찾는다.

INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
]

INSTALLED_APPS 문자열 리스트에 다음 두 문자열을 추가하고 변경 내용을 gamesapi/settings.py 파일에 저장한다.

- 'rest_framework'
- 'games.apps.GamesConfig'

다음 행에서 추가된 행을 굵게 나나낸 INSTALLED_APPS 문자열 리스트 선언의 새코드를 보여준다. 이 샘플 코드 파일은 restful_python_chapter_01_01 폴더에 들어 있다.


1.4 모델 제작
games/models.py 파일을 연다. 

Game 클래스는 django.db.models.Model 클래스으 서브 클래스다. 정의된 각 속성은 데이터베이스 열 또는 필드를 나타낸다. 장고는 모델과 관련된 데이터베이스 테이블을 생성할 때 id라는 자동 증가 정수 기본 키 열을 자동으로추가한다. 하지만 모델은 해당 모델의 pk라는 속석에 id 열을 대응시킨다. 우리는 여러 속성에 대해 필드 타입, 최대 길이, 기본값을 지정했다. 이 클래스는 정렬 속성을 선언하며 첫 벚째 값이 'name'인 문자열의 튜플로 값을 설정하는 Meta 내부 클래스를 선언했는데, 기본적으로 name 속성에 따라 오름차순으로 정렬된 결과가 나타난다.

그 다음으로는 최근에 코딩한 새 Game 모델의 초기 마이그레이션을 만들어야 한다. 다음 파이썬 스트립트를 실행하면 데이터베이스를 처음으로 동기화할 수 있다. 기봊ㄴ적으로 장고는 SQLite 데이터베이스를 사용한다. 이 예제에서는 이런 기본 구서응로 작업할 것이다.

python manage.py makemigrations games

games/migrations/0001_initaial.py 생성

이 코드는 Game 모델의 테이블을 만드는 작업을 정의하는Migration이라는 django.db.migrations.Migration 클래스의 서브 크랠스를 정의 한다. 이제, 생성된 마이그레이션을 적용하기 위해 다음 파이썬 스크립트를 실행한다.

python manage.py migrate

위의 명령을 실행하면 gamesapi 프로젝트의 루트 폴더에 db.sqlite3파일이 생긴것을 볼 수 있다. 장고가 생성한 테이블을 보려면 SQLite 명령 행 또는 SQLite 데이터베이스의 테이블을 쉽게 점검할 수 있게 해주는 애플리케이션을 사용하면 된다.

다음 명령을 실행해 생성되 ㄴ테이블을 나열해 보라.

sqlite3 db.sqlite3 '.tables'

다음 명령을 실행해 games_game 테이블을 만드는데 사용된 SQL을 알아본다.

sqlite3 db.sqlite3 '.schema games_game'

다음 명령을 사용하면 HTTP 요청을 작성해 레스트풀 API에 보내고 games_game 테이블에 CRUD 작업을 수행한 후 games_game 테이블의 내용을 점검할 수 있다.

sqlite3 db.sqlite3 'SELECT * FROM games_game ORDER BY name;'

SQLite 명령 행 유틸리티로 작업하는 대신 GUI 도구를 사용해 SQLite 테이터 베이스의 내용을 확인할 수 있다.

SQLite 데이터베이스 엔진과 데이터베이스 파일이름은 gamesapi/settings.py 파일씬 파일에 지정돼 있다. 다음 행은 장고가 사용하는 모든 데이터베이스에 대한 설정을 담고 있는 DATABASE 딕셔너리의 선언을 보여준다. 중첩된 딕셔너리는 default라는 데이터베이스를 django.db.backends.sqlite3 데이터베이스 엔진과 BASE_DIR폴더(gameapi)에 있는 db.,sqlite3 데이터베이스 파일에 대응시킨다.

games_game 테이블은 우리가 최근 생성한 Game 클래스, 특히 Game 모델의 데이터베이스 속에 유지된다. 장고의 통합 ORM은 Game 모델을 기반으로 games_game 테이블을 생성했다. games_game 테이블에는 SQLite 형식의 다음 행(필드라고도 함)이 있으며, 그 중 모두가 null 값도 가능한 것은 아니다.

- id: The interger primary key, an autoincrement row
- created: datetime
- name: varchar(200)
- release_Date: datetime
- game_category: varchar(200)
- played: bool

다음 행은 우리가 마이그레이션을 실행했을 때 장고가 생성한 SQL 생성 스크립니다.

CREATE TABLE "games_game"(
     "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
     "created" datetime NOT NULL,
     "name"  varchar(200) NOT NULL,
     "release_date"  datetime NOT NULL,
     "game_category" varchar(200) NOT NULL,
     "played" bool NOT NULL
)


장고는 나중에 사용할 웹 프레임워크와 인증 기능을 지원하는데 필요한 테이블들을 추가해 생성했다.

1.5 직렬화 역직렬화 관리
레스트풀 웹 API는 게임 인스터스를 JSON 표현은로 직렬화와 역직렬화할 수 있어야 한다. 장고 레스트 프레임워크를 ㅏ숑ㅇ하면, 게임 인스턴스가 JSON으로의 직렬화와 JSON로 부터의 역직렬화를 관리할 수 있게 직렬화기serializer 클래스를 만들어야 한다.
장고 레스트 프레임워크는 직렬화를 위해 2단계 과정을 사용한다. 직렬화기는 모델 인스턴스와 파이썬 프리미티브 사이의 중개자다. 파서parser와 렌더러renderer는 파이썬 프리미티브와 HTTP 요청과 응답 사이의 중개자로 처리한다. rest_framework.serializers.Serializer 클래스의 서브 크랠스에서 구성할 수 있는 모든것을 아게 될거 ㅅ이다. 하지만 다으 ㅁ예제에서는 나중에 사용구 코드boilerplate code를 줄일 수 있는 단추 ㄱ코드로 작업한다. ModelSerializer 클래스를 사용해 다음 예제에서의 장고 코드 양을 줄일 것이다.

이제, gamesapi/games 폴더로 이동해 serializers.py라는 새 파이썬 코드 파일을 생성한다. 다음 행은 새 GameSerializer 클래스를 선언하는 코드를 보여준다. 이 샘플 코드 파일은 restful_python_chpter_01_01폴더에 들어 있다.

GameSerializer 클래스는 직렬화할 필드를 나타내는 속성을 선언한다. 여기에는 Game모델에 존재했던 created 속성이 빠졌다는 것에 유의하라. 이 크랠스에 있어서 상속된 save 메서드 호출이 있을 때, 오버라이드한 create와 update 메서드는 인스턴스를 생성하거나 수정하는 방법을 정의한다. 사실, 이들 메서드는 베이스 선언에서 NotImplementedError 예외를 발생시키기 때문에 이 클래스에서 구현해야 한다.

create 메서든 ㄴvalidated_data 인자로 유효 데이터를 받는다. 이코드는 수신된 유효 데이터에 따라 새 Game 인스턴스를 생성해 반환한다.

update 메서드는 instance와 validated_Data 인자로 업데이트될 기존 Game 인스턴스와 새 유효 데이터를 받는다. 이 코드는 인스턴스의 속성에 대한 값을 유효 데이터에서 얻고 업데이트된 속성 값으로 업데이트 하며, 업데이트된 Game 인스턴스용 save 메서드를 호출해서 업데이트되고 저장된 인스턴스를 반환한다.

기보 ㄴ파이썬 대화형 셀을 실행해 모든 장고 프로젝트 모듈을 사용가능하게 만든 후에 이를 모듈릉 시작할 ㅜㅅ 있다. 이렇게 하면 직렬화기가 예상대로 작동하는지 점검할 수 있다. 더욱이 장고에서 직렬화가 어떻게 작동하는지 이해하는데 도움이 될 것이다. 대화형 세을 시작하려면 다음 명령을 실행하라. 이때, 터미널 또는 명령 프로프트에서 gameapi폴더 내에 위치해 있어야 한다.

python manage.py shell

기본 파이썬 대화형 셀로 들어가는 보통의 행 다음에 (InteractiveConsole)이라는 행이 나타날 것이다. 파이썬 대화영 셀에 아래 코드를 입력해 Game 모델과 이 모델의 직렬화기를 테스트 하는 데 필요한 모든 항목을 임포트한다. 이 샘플 코드는restful_python_chapter_01_01 폴더의 serializers_test_01.py 파일에 있다.

아래 코드를 입력해 Game 모델으 ㅣ두 인스턴스를 생성하고 저장하라. 이 샘플 코드도restful_python_chapter_01_01 폴더의 serializers_test_01.py파일에 들어 있다.

위의 코드를 실행한 후에는 앞서 소개한 명령 행 또는 GUI 도구로 SQLite 데이터베이스를 점검해 games_game 테이블의 내용을 확인할 수 있다. 이 테이블에는 2개의 행이 생기면 각 열에는 Game 인스턴스으 ㅣ각 속성에 제공했던 값이 들어 있을 것이다.

대화형 셀에 다음 명령을 입력해 저장된 Game 인스턴스의 기본 키 또는 식별자 값을 점검하는데, created 속성 값에는 데이터베이스에 인스턴스를 저장한 날짜와 시간이 들어간다. 이 샘플 코드 restful_python_chapter_01_01 폴더의 serailizers_test_01.py 파일에 들어있다.

이제 첫 번째 게임 인스턴스(game1)를 직렬화한느 아래 코드를 작성하자, 이 샘플 코드는 restful_python_cahpter_01_01 폴더의 serializers_test_01.py 파일에 들어있다.

다음 행은 이렇게 해서 생서된 딕셔너리, 즉 rest_Framework.utils.serializer_helpers.ReturnDict 인스턴스를 보여준다.

아래 코드로 두 번째 게임 인슨턴스(game2)를 직렬화하자. 이 샘플 코드도 restful_python_chapter_01_01 폴더의 serializers_test_01.py파일에 들어 있다.


다음 행은 이렇게 해서 생성된 딕셔너리를 보여준다.

rest_framework.renderers.JSONRenderer 클래스를 사용하면 data 속성에 저장된 딕셔너리를 JSON 으로 쉽게 만들 수 있다. 아래 행은 이 클래스의 인스턴스를 생성하고 나서 render 메서드를 호출해 data 속성에 저장된 딕셔너리를 JSON으로 만든다. 이 샘플 코드는 restful_python_chapter_01_01폴더의 serializers_test_01.py파일에 들어있다.

다음 행은 render메서드를 두 번 호출해 생성된 출력을 보여준다.

이제 직렬화된 데이터에서 Game 인스턴스의 배치로 역방향 작업을 할 것이다. 아래 행은 jSON 문자열(직렬화된 데이터)로 부터 새 GAME인스턴스를 생성, 즉 역직렬화할 것이다. 이 샘플 코드도 restful_python_chapter_01_01 폴더의 serializers_test_01.py 파일에 들어 있다.

첫 번째 행은 새 게임(json_string_for_new_game)을 정의하는 JSON으로 새 문자열을 생성한다. 그리고 나서 이 코드는 문자열을 바이트로 변환하고 json_bytes_for_new_game변수에 변환 결과를 저장한다. django.utils.siz.ByteIO 클래스는 내장 메모리 바이트 버퍼를 사용해 버퍼링된 I/O 구현을 제공한다. 여기 코드는 이 클래스로 직렬화 데이터인 json_byte_for_new_game으로 앞서 생성한 JSON 바이트로부터 스트림을 만들어 stream_for_new_game 변수에 생성된 인스턴스를 저장한다.

rest_framework.parsers.JSONParser 클래스를 사용하면 스트림을 파이썬 모델로 쉽게 역직렬화해 파싱할 수 있다. 그 다음 행에서 이 클래스의 인스턴스르르 생성하고, stream_for_new_game을 인자로 해서 parse메서드를 호출하고 스트림을 파이썬 네이티브 데이터 타입으로 파싱한 다음, 그렬과를 parsed_new_game변수에 저장한다.

위의 행을 실행한 후, parssed_new_game은 스트림으로 부터 파싱된 파이썬 딕셔너리를 저장한다. 다음 행은 위의 코드 부분을 실행한 후에 생성된 출력을 보여준다.

아래 행은 GameSerializer 클래스를 사용해 스트림에서 파싱된 파이썬 딕셔너리로부터 new_game이라는 값이 완전히 채워진 Game 인스턴스를 생성한다. 이 샘플 코드도 restful_python_chapter_01_01폴더의 serializers_test_01.py 파일에 들어 있다.

먼저, 이 코드는 data키워드 인자로 전달된 스트림(parsed_new_game)으로부터 앞서 파싱한 파이썬 딕셔너리를 사용해 GameSerializer 클래스의 인스턴스를 생성한다. 그리고 나서 is_valied 메서드를 호출해 데이터가 유효한지 알아낸다. 직렬화기의 생성에 있어서 data키워드 인자를 전달할 때 직렬화된 데이터 표현에 접근하기 전에 항항 is_valid를 호출해야 한다.

이 메서드가 true를 반환하면 data 속성에 있는 직렬화된 표현에 접근할 수 있으므로 이 코드는 save 메서드를 호출해 데이터베이스에 해당 행을 삽입하고, 값이 완전히 채워진 Game 인스턴스의 속성중 하나를 출력한다.

위의 코드를 실행한 후에는 new_game1_instance와 new_game2_instance라는 2개의 Game인스턴스가 값들로 완전히 채워졌다.

위의 코드에서 배울 수 있듯이 장고 레스트 프레임워크를 사용하면 쉽게 객체에서 JSON 으로 직렬화하고, JSON에서 객체로 역직렬화 할 수 있는데, 이런일은 CRUD 연산을 수행해야 하는 레스트 풀 웹 API에 반드시 필요한 사항이다.

직렬화와 역질열화를 테스트한 장고 프로젝트 모듈의 셀을 끝내려면 다음 명령을 입력한다.
quit()

1.6 API 뷰 작성
이제 앞서 생성했던 GameSerializer 클래스를 사용해 API가 처리할 각 HTTP 요청에 대해 JSON 표현을 반환하는 장고 뷰를 만들 것이다. games/views.py 파일을 연다. 다음 행은 이 파일의 초반 코드를 보여주는데, 단 하나의 import 문과 함께 뷰를 만들라는 주석이 있다.

아래 행은 games/views.py 파일에 넣을 새 코드를 보여주는데 JSONResponse 클래스를 만들고 game_list와 game_detail이라는 2개 함수를 선언한 것이다. 여기서는 API의 첫 번째 버전을 만들 것이며, 이들 함수를 사용해 코드를 최대한 단순하게 만든다. 다음 예제에서는 클래스와 함께 좀 더 복잡한 코드로 작업할 것이다. 굵게 나타낸 행은 HTTP 동사에 따라 수행할 작업을 결정하기 위해 request.method속성 값을 평가하는 표현식을 나타낸 것이다. 이 샘플 코드 파일은 restful_python_chapter_01_01 폴더에 들어 있다.

JSONResponse 클래스는 django.http.HttpResponse 클래스의 서브 클래스다. 이 슈퍼 클래스는 내용이 문자열로 된 HTTP 응답을 나타낸다. JSONResponse 클래스는 내용을 JSON으로 렌더링한다. 이 클래스에서는 __init__ 메서드만 선언하는데, 이 메서드는  rest_framework.renderers.JSONRenderer 인스턴스를 생성해 render 메서들ㄹ 호출해서는 수신된 데이터를 JSON으로 렌더링한 후 반환된 바이트 분잘열을 CONTENT 로컬 변수에 저장한다. 그러고 나서 'application/json'의 응답 헤더에 'content_type'키를 값으로써 추가한다. 마지막으로, JSON바이트 문자열과 헤더에 추가된 키-값 쌍으로 베이스 클래스의 초기자initializer를 호출한다. 이렇게 해서 이 클래스는 두 함수를 통해 JSON응답을 쉽게 반환한다.

이 코드는 두 함수에 @csrf_exempt 데커레이터를 적용해 뷰가 사이트 간 요청 위조(CSRF,Cross_Site Request Forgery) 쿠키를 설정하게 한다. 이를 통해 제품 준비 상태의 웹 서비스를 나타내지 않는 이 예제에 대해 테스트를 단순화시킨다. 레스트풀 API에 보안 기능은 나중에 추가할 것이다.

장고 서버가 HTTP 요청을 받으면 장고는 HttpRequest 인스턴스, 특히 django.http.HttpRequest 객체를 생성한다. 이 인스턴스에는 HTTP 동사를 비롯한 요청에 대한 메타 데이터가 들어간다. method 속성은 요청에 사용된 메서드 또는 HTTP 동사를 나타내는 문자열을 제공한다.

장고는 요청을 처리할 적절한 뷰를 로드할 때, HttpRequest 인스턴스를 뷰 함수의 첫 번째 인자로 전달한다. 뷰 함수는 HttpResponse 인스턴스, 특히 django.http.HttpResponse 인스턴스를 반환해야 한다.

game_list 함수는 모든 게임을 나열하거나 새 게임을 생성한다. 이 함수는 request인자로 HttpRequest 인스턴스를 받는다. 이 함수는 GET 및 POST의 두 가지 HTTP동사에 따라 실행할 코드를 달리한다. HTTP 동사가 GET이라면, request.method == 'GET' 표현식이 True가 돼 모든 게임을 나열한다. 이 코드는 데이터베이스에서 모든 Game 객체를 얻고 GameSerializer를 사용해 모두 직렬화하며 GameSerializer에서 생성한 데이터로 작성된 JSONResponse 인스턴스를 반환한다. many = True 인자로 GameSerializer 인스턴스를 생성하면 여러 인스턴스를 직렬화할 것을 지정한 것이 된다. many 인자 값을 True로 설정하면 장고는 내부적으로 ListSerializer를 사용한다.

HTTP동사가 POST라면, HTTP 요청에 포함된 JSON 데이터로 새 게임을 생성한다. 먼저 JSONParser 인스턴스를 사용하고 request를 인자로 받는 parse 메서드를 호출해서 요청 속에 들어 있는 JSON으로 된 게임 데이터를 파싱해 그 결과를 game_data 로컬 변수에 저장한다. 그리고 나서 이렇게 저장된 데이터로 GameSerializer 인스턴스를 생성하고 is_valid메서드를 호출해 Game인스턴스가 유효한지 알아낸다. 그 인스턴스가 유효하면 save메서드를 호출해 인스턴스를 데이터베이스에 저장하고, 이 저장된 데이터와 함께 status.HTTP_201_CREATED인상태, 즉 201 Created로 나타나는 상태를 담은 JSONResponse를 반환한다.

200 OK 상태와는 다른 특성 상태를 반환해야 할 때면, rest_framework.status 모델에서 정의한 모듈 변수를 사용하고 고정된 숫자 값은 사용하지 않는 것이 좋다.

game_detail 함수는 기존 게임을 검색, 업데이트, 삭제한다. 이 함수는 reqeuset 인자로 HttpRequest 인스턴스를 받고 pk 인자로 검색, 업데이트, 삭제할 게임의 기본 키 또는 식별자를 받는다. 이 함수는 GET, PUT, DELETE의 세가지 HTTP 동사를 처리할 수 있다. 이 코드는 request.method 속성의 값을 점검해 HTTP 동사에 따라 실행할 코드를 결정한다. HTTP 동사가 무엇이든 관계없이 이 함수는 받은 pk를 pk 인자로 해서 Game.objects.get메서드를 호출해 지정된 기본 키 또는 식별자에 따라 데디터베이스에서 Game 인스턴스를 찾아 game 로컬 변수에 저장한다. 지정된 기본 키 또는 식별자의 게임이 데이터베이스에 없는 경우에는 status.HTTP_404_NOT_FOUND와 동일한 상태, 즉 404 Not Found 가 들어간 HttpResponse를 반환한다.

HTTP 동사가 GET이라면, game을 인자로 해서 GameSerializer인스턴스를 생성하고 기본적인 200 ok 상태가 들어간 JSONResonse를 통해 질결화된 게임 데이터를 반환한다. 가져온 JSON 직렬화 게임 데이터를 반환한 것이 된다.

HTTP동사가 PUT이라면, HTTP 요청에 포함된 JSON 데이터로 새 게임을 만들어 기존 ㅔㄱ임을 대체하게 된다. 먼저, JSONParser 인스턴스를 사용하고 reqeuset를 인자로 해서 parse메서드를 호출해 요청에서 JSON 데이터로 제공된 게임 데이터를 파싱한 후 그 결과를 game_data 로컬변수에 저장한다. 그리고 나서 앞서 데이터베이스로부터 얻은 Game 인스턴스와 기존 데이터(game_data)를 대체할 검색된 데이터롤 GameSerializer 인스턴스르 생성한다. 그런 다음, is_valid메서들ㄹ 호출해 Game인스턴스가 유효한지 점검한다. 인스턴스가 유효하면, save 메서드를 호출해 데이터베이스에 바뀐 값으로 인스턴스를 저장하고 저장된 데이터가 있는 본문과 기본적인 200 ok 상태가 들어간 JSONResponse를 반환한다. 파싱된 데이터로 유효한 Game인스턴스가 생성되지 않으면, status.HTTP_400_BAD_REQUEST와 동일한 상태, 즉 400 Bad Request가 들어간 JSONResponse를 반환한다.

HTTP동사가 DELETE라면, 앞서 데이터베이스로부터 얻은 Game 인스턴스(game)에서 delete메서드를 호출한다. delete 메서드를 호출하면 games_game테이블의 기본 행이 지워지므로 해당 게임을 더 이상 이용할 수 없게 된다. 그리고 나서 status.HTTP_204_NO_CONTENT와 같은 상태, 즉 204 No Content가 들어간 JSONResonse를 반환한다.

이제 games폴더에 urls.py라는 새 파이썬 파일, 즉 games/url.py파일을 만들어야 한다. 아래 행은 이 파일에 대한 코드이면, view.py 파일에 정의한 특정 함수를  실행하기 위해 해당되는 정규 표현식을 요청에 지정하는 URL 패턴을 정의한다. 이 샘플 코드 파일은 restful_python_chapter_01_01폴더에 들어 있다.

urlpattern리스틀 사용하면 URL을 뷰로 보낼 수 있다. 이코드는 해당 정규 표현식과 함께 뷰 모듈에서 정의한 뷰 함수를 인자로 해서 django.conf.urls.url 함수를 호출해 urlpatterns리스트의 각 항목에 대해 RegrexURLPattern인스턴스를 생성한다.

gamesapi 폴더의 urls.py 파일, 즉 game/urls.py 파일을 만들어야 한다. 아래 행은 이 파일에 대한 코드이며, views.py 팡리에 정의한 특정 함수를 실행하기 위해 해당되는 정규 표현식을 요청에 지정하는 URL 패턴을 정의 한다. 이 샘플를 코드파일은 restful_python_chapter_01_01폴더에 들어 있다.

urlpatterns 리스트를 사용하면 URL을 뷰로 보낼수 있다.  이 코드는 해당 정규 표현식과 함께 뷰 모듈에서 정의한 뷰 함수를 인자로 해서 django.conf.urls.url 함수를 호출해 urlpatterns리스트의 각 항목에 대해 RegexURLPattern 인스턴스를 생성한다.

gamesapi 폴더의 urls.py 파일, 즉 gamesapi/urls.py 팡리의 코드는 변경해야 한다. 이파일은 로트 URL 구성을 저의하므로 위에서 코딩한 gamesapi/urls.py 파일에 선언된 URL 패턴을 포함시켜야 한다. 아래 행은 gameapi/urls/.py 파일의 새 코드를 보여준다. 이 샘플 코드 파일도 restful_python_chapter_01_01폴더에 들어 있다.

이제 장고의 개발 서버를 시작해 HTTP요청을 작성하고 비보안 웹 API에 그 요청을 보낼수 있다. 다음 명령을 실행하라.

python manage.py runserver 0.0.0.0:8000

LAN에 연결된 다른 컴퓨터 또는 장치에서 HTTP 요청을 작성해 전송하려면, localhost 대신 개발 컴퓨터의 할당된 ip 주소를 사용해야 한다는 점에 유의하라. 예를 들어, 컴퓨터의 할당된 IPV4용 IP주소가 locahost:8000 대신 192.168.1.106인 경우 192.168.1.106:8000을 사용해야 한다.


1.7 API에 대한 HTTP 요청

장고 개발 서버는 localhost에서 실행 중이고, 포트 8000에서 리스닝하며 HTTP요청을 기다린다. 이제 개발 컴퓨터 똔느 LAN에 연결된 다른 컴퓨터 또는 장치에서 로컬로 HTTP 요청을 작성해 보낼 것이다. 우리는 다음과 같은 여러 종류의 도구를 사용해 이 책에서의 HTTP 요청을 작성하고 보낼 것이다.

- 명령 행 도구
- gui 도구
- 파이썬 코드
- 자바스크립트  코드

1.8 명령행 도구로 작업 - curl과 httpie
명령 행 도구부터 시작할 것이다. 명령 행 도구의 주요 장점 중 하나는 최초에 HTTP요청을 빌드한 후 쉽게 이 요청을 다시 실행할 수 있으므로 굳이 마우스를 사용하저나 화면을 탭해 요청을 실행하지 않아도 된다는 것이다. 배치 요청들이 들어간 스크립트를 쉽게 만들어 실행할 수도 있다. 모든 명령 행 도구에서 일어나듯이 GUI 도구에 비해 최초 요청을 수행하는데 보다 많은 시간이 걸릴 수 있지만, 일단 낳은 요청을 수행하고 나면 이전에 작성한 명령을 쉽게 다시 사용해 새 요청을 작성할 수 있다.

curl은 cURL이라고 하며, 쉽게 데이터를 전송할 수 있는 아주 유명한 오픈소스 명령 행 도구이자 라이브러리다. curl 명령행 도구를 사용해서도 HTTP 요청을 쉽게 작성하고 봰며 응답을 점검할 수 있다.

맥OS또는 리눅스에서 작업할 겨웅, 터미널을 열고 명령 행에서 curl을 사요할 수 있다. 모든 위도우 버젼에서 작업할 경우, Cygwin패키지를 설치 옵션에서 curl을 쉽게 설치하고 Cygwin터미널에서 실행할 수 있다.

curl -X GET :8000/games/

위의 명령은 GET http://localhost:8000/games/라는 HTTP 요청을 작성해 보낼 것이다. 이 요청은 views.game_list 함수, 즉 games/views.py 파일 내에서 선선한 game_list 함수를 찾아 실행하므로 레스트 풀 API에서 가장 간단한 경우다. 이 함수는 URL패턴에 매개 변수가 없으므로 request를 매개 변수로 받는다. 요청에 대한 HTTTP동사가 GET이기 때문에 request.method 특성은 'GET'과 같으므로 이 함수는 모든 Game 객체를 얻어 JSON 응담을 생성하는 코드를 실행할 것인데, 이렇게 생성된 응답에는 이들 Game 객체 모두가 직렬화된 상태로 들어 있게 된다.

다음 행은 HTTP 요청에 대한 응답의 예를 보여주는데, 이 json응답에는 3개의 Game 객체가 들어 있다.

curl -ix GET :8000/gaems/



















댓글 없음: