본문 바로가기
python

django를 이용한 REST API 서버 구성

by misankim 2023. 3. 19.

지난번 php, node.js 로 각각 구성해봤던 백업 결과 api 서버를 이번에는 파이썬 기반의 웹프레임워크인 django를 이용하여 REST API 서버로 구성해보는 내용으로 학습을 진행하였습니다. 이번에는 기존 php, node.js 로 구성했던 방식과 달리 django에서 쉽게 REST API를 구성할 수 있는 djangorestframework를 사용하였습니다.

1. 개념 설명

1) django

파이썬 기반의 웹프레임워크 중 flask와 함께 가장 많이 쓰이는 웹프레임워크

2) djangorestframework

django 프레임워크에서 웹 API를 구축하기위한 툴킷(https://www.django-rest-framework.org/)

3) django-rest-swagger

Django REST 프레임워크용 Swagger/OpenAPI 문서 생성기(https://django-rest-swagger.readthedocs.io/en/latest/)


2. 구성

1) 테스트 구성 환경

django 에서는 기본 DB 엔진으로 sqlite를 사용하는데 centos 7 에서 yum 설치 제공하는 sqlite 버전이 낮은 문제로, 수동 설치해도 안잡혀서 centos 8 도커 이미지로 테스트 구성했습니다. 굳이 도커를 사용하지 않아도 python 및 django 실행에 필요한 의존성 패키지의 버전만 지원된다면 우분투 환경이나, 윈도우 환경에서 진행해도 관계 없습니다.

# 컨테이너 생성
docker run -d --network wp -v /root/python/:/root/python/ -p 8000:8000 --name python centos:8 /sbin/init

# 컨테이너 접속
docker exec -it python bash

2) python 설치

편의상 yum으로 설치했지만 소스컴파일해서 설치해도 관계 없습니다.

yum install -y python3 python3-devel

echo "alias python='python3'" >> ~/.bashrc
source ~/.bashrc

3) django 프레임워크 및 djangorestframework, django-rest-swagger 설치

python -m pip install django
python -m pip install djangorestframework
python -m pip install django-rest-swagger

4) 프로젝트 생성 및 rest_framework 추가

프로젝트 - 전체 웹사이트
앱 - 서브 모듈

테스트 환경에서는 프로젝트의 이름을 testapp 으로 임의로 설정했습니다.

cd /root/python

django-admin startproject testapp

cd testapp

vim /root/python/testapp/testapp/settings.py

기존 INSTALLED_APPS 항목에 rest_framework, rest_framework_swagger 두 줄 추가
(INSTALLED_APPS - 현재 Django 인스턴스에서 활성화된 모든 Django 어플리케이션)

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

    'rest_framework',
    'rest_framework_swagger',
]

5) 앱 생성

python manage.py startapp backup

vim /root/python/testapp/testapp/settings.py

방금 생성한 backup 앱을 INSTALLED_APPS에 추가

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

    'rest_framework',
    'rest_framework_swagger',
    'backup',
]

6) mysql 연동

django 는 기본값으로 sqlite를 DB 엔진으로 사용하기 때문에 다른 DB 엔진을 사용하려면 설정 수정 필요

# DB, 접속 정보 생성(mysql 에서 작업)
create database django_DB;
grant all privileges on django_DB.* to 'django_user'@'%' identified by 'wkdrh!';

테이블(backup_backup)은 migrate 작업 시 backup/models.py 파일에 정의한 Backup 클래스에 의해 자동으로 생성됨

# django 에서 작업
yum install -y gcc mysql-devel python3-devel
python -m pip install mysqlclient

vim testapp/settings.py

기존 내용

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

아래와 같이 수정(상황에 따라 적절히 설정할 것)

DATABASES = {
    'default' : {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_DB',
        'USER': 'django_user',
        'PASSWORD': 'wkdrh!',
        'HOST': 'mysql.wp',
        'PORT': '3306',
    }
}

7) 백업 DB 모델 생성 및 마이그레이션

모델은 데이터에 대한 정보 소스로, 일반적으로 각 모델은 단일 데이터베이스 테이블에 매핑(https://docs.djangoproject.com/en/3.1/topics/db/models/)

마이그레이션은 모델에 대한 변경 사항 (필드 추가, 모델 삭제 등)을 데이터베이스 스키마에 전파하는 Django의 방법(https://docs.djangoproject.com/en/3.1/topics/migrations/)

vim backup/models.py

from django.db import models

class Backup(models.Model):
   NUM = models.AutoField(primary_key=True)
   Date = models.CharField(max_length=20)
   Hostname = models.CharField(max_length=30)
   PRI_IP = models.CharField(max_length=20)
   PUB_IP = models.CharField(max_length=20)
   Result = models.CharField(max_length=20)
   File_check = models.CharField(max_length=30, null=True)
   start_date = models.DateTimeField(auto_now_add=True, null=False)
   end_date = models.DateTimeField(auto_now=True, null=True)

저장 후 나가기

# DB 모델 마이그레이션 생성 및 적용
python manage.py makemigrations backup
python manage.py migrate backup

python manage.py shell -> 새로운 파이썬 쉘이 열리면 아래 라인 입력

from backup.models import Backup
Backup.objects.create(Date='20201119', Hostname='premisan-test123', PRI_IP='10.10.10.10', PUB_IP='123.123.123.123', Result='success', File_check='300/300,100%')

8) api 클래스 작성 및 url 파일 작성

api 가 사용할 클래스와 각 경로별로 어떤 페이지가 보여질지 작성

vim backup/api.py

from .models import Backup
from rest_framework import serializers, viewsets

class BackupSerializer(serializers.ModelSerializer):

    class Meta:
        model = Backup
        fields = '__all__'

class BackupViewSet(viewsets.ModelViewSet):
    queryset = Backup.objects.all()
    serializer_class = BackupSerializer

저장 후 나가기
vim testapp/urls.py

from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from rest_framework_swagger.views import get_swagger_view

import backup.api

app_name='backup'

router = routers.DefaultRouter()
router.register('backups', backup.api.BackupViewSet)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/doc', get_swagger_view(title='Rest API Document')),
    url(r'^api/v1/', include((router.urls, 'backup'), namespace='api')),
]

9) 호환성 관련 에러 수정

아래 내용은 버전 관련 호환성 문제로 에러 발생하는 부분이 있어 수정한 것

vim testapp/settings.py

아래 내용 추가

REST_FRAMEWORK = {
   'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
}

저장 후 나가기
vim /usr/local/lib/python3.6/site-packages/rest_framework_swagger/templates/rest_framework_swagger/index.html

2번째 줄의 {% load staticfiles %} 를 {% load static %} 으로 수정

10) ALLOWED_HOSTS 값 추가(상황에 따라)

django 서버가 현재 PC가 아닌 다른 외부 서버 등에 있는 경우 ALLOWED_HOSTS 설정을 추가해야함

vim /root/python/testapp/testapp/settings.py

28번째 줄에 ALLOWED_HOSTS 값 추가(테스트 환경의 접속 도메인은 www.premisan01.shop)

ALLOWED_HOSTS = ['www.premisan01.shop']

11) 실행 및 접속 테스트

# 모든 앱에 대한 마이그레이션 사항을 적용
python manage.py migrate

# 서버 구동
python manage.py runserver 0.0.0.0:8000

# 접속 테스트
http://www.premisan01.shop:8000/api/v1/backups/

# api 문서 접속
http://www.premisan01.shop:8000/api/doc

# 리스트 조회
curl -H "Content-Type: application/json" -X GET http://www.premisan01.shop:8000/api/v1/backups/ | jq

# 백업 시작
result_NUM=`curl -H "Content-Type: application/json" -X POST -d "{\"Date\": \"$(date +%Y%m%d)\",\"Hostname\": \"$HOSTNAME\", \"PRI_IP\": \"$(ifconfig eth0 | head -2 | tail -1 | sed 's/ //g' | awk -Finet '{ print $2 }' | awk -Fnetmask '{ print $1 }')\", \"PUB_IP\": \"$(curl -s ifconfig.me)\", \"Result\":\"running\"}" http://www.premisan01.shop:8000/api/v1/backups/ | jq -r .NUM`

# 백업 완료
curl -H "Content-Type: application/json" -X PUT -d "{\"Date\": \"`date +%Y%m%d`\",\"Hostname\": \"$HOSTNAME\", \"PRI_IP\": \"`ifconfig eth0 | head -2 | tail -1 | sed 's/ //g' | awk -Finet '{ print $2 }' | awk -Fnetmask '{ print $1 }'`\", \"PUB_IP\": \"`curl -s ifconfig.me`\", \"Result\": \"success\", \"File_check\": \"300/300,100%\"}" http://www.premisan01.shop:8000/api/v1/backups/${result_NUM}/ | jq

3. djangorestframework를 통해 구성한 api 와 기존 php, node.js 와의 차이점

REST API의 가장 핵심적인 특징은 아래 두 가지입니다.

첫 번째, URI는 정보의 자원을 표현해야 한다.
두 번째, 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현한다.

https://meetup.toast.com/posts/92

이와 비교하여 지난 php, node.js api는 api_start, api_end 등과 같이 URI를 통해 행위를 구분하여 구성되어 있는 반면, django 를 통해 구성한 api는 /api/v1/backups/ 라는 리소스(URI)에 대해 메소드(GET, POST, PUT)로 행위를 구분하고 있습니다.

언어 행위 URI 메소드
php 백업 시작 /backup_result/api_start.php POST
php 백업 완료 /backup_result/api_end.php POST
js 백업 시작 /backup_result/api_start POST
js 백업 완료 /backup_result/api_end POST
python 리스트 확인 /api/v1/backups/ GET
python 백업 시작 /api/v1/backups/ POST
python 백업 완료 /api/v1/backups/${NUM}/ PUT

4. 결론

django와 djangorestframework를 통해 REST API 서버를 쉽게 구성해봤습니다. 초보자 수준이지만 동일한 역할의 서버를 여러 언어/웹프레임워크를 이용하여 구성해보는 데에 의미가 있었던 것 같습니다.