지난번 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 서버를 쉽게 구성해봤습니다. 초보자 수준이지만 동일한 역할의 서버를 여러 언어/웹프레임워크를 이용하여 구성해보는 데에 의미가 있었던 것 같습니다.
'python' 카테고리의 다른 글
flask 기반 웹에서 AWS Rekognition 서비스 활용 및 S3 버킷으로 파일 업로드 (0) | 2023.03.20 |
---|---|
django REST API 서버에 토큰 인증 적용 (0) | 2023.03.19 |