[Django] 간단한 endpoint 구현해보기

ybear90·2020년 2월 10일
0

Django

목록 보기
6/12

이번에는 django를 본격적으로 사용하는 포스트인데 실제 프로젝트를 하기 앞서 먼저 HTTP 통신으로 데이터를 주고받을 수 있는 가능한 간단한 프로젝트를 진행해 보고 알고 있는 내용을 체크해 본다.

1. Django project 생성 및 view의 최소 뼈대 생성

([virtualenv_name]) $ django-admin startproject simple_end

(가상환경을 설정할 수 있다면 설정한 상태에서) 위와 같은 명령어로 프로젝트를 만들게 되면

simple_end
├── manage.py
└── simple_end
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

위와 같은 형태의 트리 구조의 django project를 생성할 수 있다. 여기서 대표적으로 project를 만들어 내기 위해 다음 3개의 프로젝트 파일에 대해서 작업을 수행한다.

  • urls.py : URLConf 파일(URL부분을 정의해주는 부분), 필자는 여기서 / 위치 때문에 종종 실수(패턴주의)
  • settings.py : django project 관련 설정 정보를 담고 있는 파일
  • (app 생성 후 app 내에서) models.py : Django ORM을 이용하여 DB 테이블을 정의하는 파일
  • (app 생성 후 app 내에서) views.py : 화면에 보여줄 수 있도록 데이터의 처리 및 로직을 수행하는 파일(이번 포스트의 핵심이 됨)

** 이 프로젝트에서 화면을 나타내는 template을 만들지 않는 이유 : 주로 data의 모델링 HTTP 통신 규격에 맞춰 주고 받는 api를 만들 것이며 화면을 나타내는 부분은 전적으로 Front-end에 의존한다고 가정하기 때문

해당 프로젝트 경로로 들어가서 본격적으로 endpoint app을 만든다.

$ python manage.py startapp endpoint
endpoint
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

views.py 를 열어 다음과 같이 작성한다(class 기반의 view 작성)

# endpoint/views.py
import json
from django.views import View
from django.http	import JsonResponse

class MainView(View):
  	# get 요청 처리를 위한 View 클래스의 get메소드 overriding
  	def get(self, request):
    	return JsonResponse({"Hello":"World"}, status=200)
# endpoint/urls.py
from django.urls 	import path
from main.views		import MainView

# django에서 경로를 명시할 때 urlpatterns 리스트의 패턴을 항상 참고한다
urlpatterns = [
  # as_view()메소드의 역할 : 해당 view의 주소를 호출하면 HTTP method가 GET인지 POST인지 DELETE인지 
  # UPDATE인지 판별해서 그에 맞는 함수를 실행
  path('', MainView.as_view())
]
# simple_end/urls.py
from django.urls import path, include

urlpatterns = [
    path('endpoint', include('endpoint.urls'))
]

class 기반 view안에 get(HTTP GET 역할을 하는 메소드라 생각하면 된다)을 정의하고 일단 url을 통해 해당 view에 GET요청을 할 시 Hello World가 화면에 보여지게 구성했다 실제 결과는 아래와 같다.

결과를 확인하기 위한 서버 구동

$ python manage.py runserver

실제 localhost:8000(runserver 기본값)에 찍혀진 내용

{"Hello": "World"}

httpie 를 통한 HTTP 통신 요청 예제

$ http -v http://127.0.0.1:8000/endpoint

GET /endpoint HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.0.0



HTTP/1.1 200 OK
Content-Length: 18
Content-Type: application/json
Date: Fri, 07 Feb 2020 12:14:39 GMT
Server: WSGIServer/0.2 CPython/3.8.1
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "Hello": "World"
}

결과를 설명하자면 이렇다

  • 웹 브라우저를 통해 또는 httpie등과 같이 client단에서 올바른 url로 http 통신을 하게 되면(GET)
  • 서버단에 해당하는 MainViewget()메소드를 통해 JSON 형태인 "Hello": "World"를 리턴하게 된 것이다
  • 직접적으로 응답을 한 view.py 부분을 통신을 하는 끝단 즉 endpoint라고 정의한다

해당 예제는 가장 간단하게 구현할 수 있는 django 기반의 endpoint가 된다.

(추가) Httpie란 ?

HTTP메소드를 전송할 수 있는 쉘 프로그램 아래와 같이 설치하고 사용하면 된다

# Ubuntu
$ sudo apt-get install httpie

# Macos(homebrew)
$ brew install httpie

GET Method 전송 예제

$ http -v <http-전송을 요청할 URL>

POST Method 전송 예제

$ http -v <http-전송을 요청할 URL> [data1]=[value1] [data2]=[value2] ... ...

2. 모델 추가 및 설정 정리

이제는 가상이지만 계정을 만들어 로그인 하는 상황을 가정해 보자. 회원 정보를 담을 테이블을 다음과 같이 models.py에 정의한다. 아래처럼 정의했다.

from django.db import models

class Users(models.Model):
		name						=	models.CharField(max_length=50)
    email						= models.CharField(max_length=50)
    password				= models.CharField(max_length=300)
    created_at			= models.DateTimeField(auto_now_add=True)
    updated_at			= models.DateTimeField(auto_now=True)
# 그전에 settings.py에서 INSTALLED_APP 항목에 만들어 준 app을 추가해준다
# 그렇지 않으면 새롭게 만들어진 app이 제대로 인식되지 않아 data modeling이 진행되지 않는다
# settings.py 중 일부
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'endpoint', # 이 부분을 추가해 준다.
]

위와 같이 만든 후 아래와 같은 명령어를 통해 migration을 진행해 준다.

$ python manage.py makemigrations
Migrations for 'endpoint':
  endpoint/migrations/0001_initial.py
    - Create model Users
$ python manage.py migrate endpoint 0001
Operations to perform:
  Target specific migration: 0001_initial, from endpoint
Running migrations:
  Applying endpoint.0001_initial... OK
  • migrate부분에서 app명칭과 migration file number를 명시해준 이유 : 새롭게 모델링 한 데이터를 하나씩 migrate를 해야 복잡하고 거대한 데이터 모델 구조일 때 부하도 적어지고 문제가 생길 여지가 줄어든다 문제가 생겼어도 해당 데이터 모델만 확인하면 되니깐 유지보수에도 용이하다.(일종의 습관적 문제?)

다음으로 만든 모델을 기반으로 post()가 가능한 View제작을 해본다.

import json
from django.views		import View
from django.http		import JsonResponse
from .models				import Users

class MainView(View):
  	def post(self, request):
      	# HTTP 통신으로 전달 받은 데이터를 json의 형태로 load하는 함수
      	data = json.loads(request.body)
        Users(
        		name			= data['name'],
          	email			= data['email'],
          	password	= data['password']
        ).save()
        
        # 제대로 응답처리 했다면 200 status와 함께 성공 메세지 전달
        return JsonResponse({'message':'SUCCESS'}, status=200)
    
    def get(self, request):
      	return JsonResponse({'Hello':'World'}, status=200)

새롭게 제작한 부분이 post()부분인데 받아온 데이터를 Users라는 모델 클래스의 형태로 모델 객체를 생성하여 저장하고 저장이 제대로 완료 되었다면 성공했다는 메세지를 띄우게 구성했다.

위에 POST 메소드 작성 예제를 참고하여 POST 테스트를 해보았을 때 정상적인 결과가 출력된다면 아래와 같이 나온다

February 07, 2020 - 12:52:28
Django version 3.0.2, using settings 'simple_end.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Forbidden (CSRF cookie not set.): /endpoint
[07/Feb/2020 12:52:53] "POST /endpoint HTTP/1.1" 403 2864

위와 같이 에러가 뜰텐데 CSRF cookie 처리가 되지않은 데이터가 post가 일어나기 때문에 django 웹서버에서 사전 차단한 것이다. 그렇다면 왜 뭐 때문에 저런 설정이 어디 되어 있단 말인가 ? 답은 settings.py에 있었다.

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware', <- 이 부분 주석처리
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

django는 csrf 보안 사고를 막기 위해 데이터를 받아올 때 'django.middleware.csrf.CsrfViewMiddleware'라는 middleware 모듈이 해당 문제를 자동으로 처리해 주게 되어 있다. 일단 endpoint 통신 자가 테스트를 하는 와중이므로 과감하게 주석 처리해 주면 된다.

그러고 나서 httpie를 통해 post()메소드 작동을 체크해보면 다음과 같다.

$ http -v http://127.0.0.1:8000/endpoint name=test email=test@test.com password=1234

POST /endpoint HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 62
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.0.0

{
    "email": "test@test.com",
    "name": "test",
    "password": "1234"
}

HTTP/1.1 200 OK
Content-Length: 22
Content-Type: application/json
Date: Fri, 07 Feb 2020 13:10:36 GMT
Server: WSGIServer/0.2 CPython/3.8.1
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "SUCCESS"
}

정상적으로 POST 응답이 진행되는 것을 확인할 수 있다. 그렇다면 POST(DB에)한 데이터를 get()메소드를 통해 다시 한 번 가져와 보자.

# endpoint/views.py
# 데이터를 받아오기 위해 아래와 같이 수정
def get(self, request):
  	user_data = Users.objects.values() # ORM 메소드를 통해 DB에서 데이터를 가져옴
  	return JsonResponse({'users':list(user_data)}, status=200)
GET /endpoint HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.0.0



HTTP/1.1 200 OK
Content-Length: 168
Content-Type: application/json
Date: Fri, 07 Feb 2020 13:20:20 GMT
Server: WSGIServer/0.2 CPython/3.8.1
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "users": [
        {
            "created_at": "2020-02-07T13:10:36.976Z",
            "email": "test@test.com",
            "id": 1,
            "name": "test",
            "password": "1234",
            "updated_at": "2020-02-07T13:10:36.976Z"
        }
    ]
}

정상적으로 가져 왔다면 위와 같은 결과를 얻어올 수 있다.

Reference

  • Wecode 과정에서 진행한 django endpoint 기초 내용을 요약 정리.
profile
wanna be good developer

0개의 댓글