on
Django tutorial
Django
setup
- 가상환경 생성
python3 -m venv VenvDjango
cd VenvDjango/ && source bin/activate
- 프로젝트 디렉토리 파기
mkdir django-drf-react-quickstart && cd $_
- 장고 & 장고 rest framework 깔기, 프로젝트 시작
pip install django djangorestframework
django-admin startproject project
Django Application 만들기
Django는 많은 Application들 로 구성되있다. 각 application은 이상적으로는 한가지 일만 해야한다. Django application 들은 모듈화되있고 재사용가능하다. Post를 작성하고 리스팅하는 application을 만들 수 있다. 다른 프로젝트가 같은 앱을 필요로하면 난 해당 앱을 패키지 매니저로 부터 설치하면된다.
이전에 만든 startproject 를 통해 project라는 폴더가 생성되 있었을텐데, 해당 폴더에 들어가서
django-admin startapp app_name
아래 명령어로 application 을 생성해주도록 한다. 여기선 app_name을 leads라고 하겠다.
그럼 프로젝트 구조는 django-drf-react-quickstart 아래
- project |— leads |— project |— manage.py
이 구조가 된다. 이때 project/settings.py에
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'leads', # 방금 생성한 app 을 추가한다
]
이제 첫번째 모델을 추가해보자.
Django Model 만들기
첫번째 model을 만들 차례다. model은 테이블의 데이터를 표현하는 객체이다. 장고 뿐만 아니라 거의 모든 웹 프레임워크가 모델의 개념을 가지고 있다. 장고 모델은 1개 이상의 Field를 가지는데, Field는 테이블의 Column(열)을 의미한다.
예시 모델은 3개의 Field를 가진다고 해보자.
- name
- message
Django는 자동으로 created_at column을 추가해주지 않으므로 누락하지 않도록 한다.
leads/models.py를 열고 lead model을 작성한다.
from django.db import models
class Lead(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
message = models.CharField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
cf. 모델 field에 대한 자세한 documentation Use-case에 가장 적합한 필드를 고르도록한다.
모델을 작성했으니, migration 을 만들어본다.
python manage.py makemigrations leads
그리고 db에 migrate한다.
python manage.py migrate
Testing
테스팅을 위해 아래 패키지를 설치한다
pip install coverage
coverage run --source='.' manage.py test
// run converage
coverage html
// report 를 html 로 보고싶을때
coverage report
// command line에서 보고 싶을때
project/htmlcov/index.html을 browser에서 열어보면 뭘 테스트해야하는지 알수있을 것이다.
REST serializer
serialization은 객체를 어떤 데이터 포맷으로 변형시키는 것이다. 그렇게 하면 파일로 저장하거나 네트워크를 통해 보낼 수 있는 형태가 된다. Django에서도, 모델이 Python class이고 이를 브라우저에서 렌더링하기 위해 JSON으로 바꿔야하기 때문에 Django REST Serializer가 필요하다. 이는 JSON을 객체로 바꾸는 데에도 적용된다.
./leads/serializer.py라는 파일을 만들고, 아래와 같이 작성한다.
from rest_framework import serializers
from leads.models import Lead
class LeadSerializer(serializers.ModelSerializer):
class Meta:
model = Lead
fields = ('id', 'name', 'email', 'message')
field를 한땀한땀 작성하지 않고,
class LeadSerializer(serializers.ModelSerializer):
class Meta:
model = Lead
fields = '__all__'
이렇게 작성할 수도 있다. 이제 Views/Urls를 살펴보자
Controller가…아니고 Views
다른 프레임워크를 쓰다보면, Django에 Controller가 없다는 게 꽤 놀랍다. 보통 컨트롤러는 Request를 처리하고 Response를 내보내는 로직을 포함한다. 전통적인 MVC 아키텍쳐에서는 Model, View, Controller 세 개가 존재한다. Rails등이 있다. Django는 MVT 프레임워크인데, Model, View, Template이다. View가 Response/Request 라이프사이클을 관리한다. Django에는 Function View, Class based view, generic view 같이 다양한 종류의 View가 존재한다.
우리는 여기서 generic API views를 기준으로 실습을 진행한다.
- 모델의 collection을 조회하고,
- DB에 신규 객체들을 생성한다
generics api 내에는 조회와 생성을 위한 view가 존재하는데, 아래와 같은 식으로 사용하면 된다 ./leads/views.py 을 열고
from leads.models import Lead
from leads.serializers import LeadSerializer
from rest_framework import generics
class LeadListCreate(generics.ListCreateAPIView):
queryset = Lead.objects.all()
serializer_class = LeadSerializer
3줄만 추가했는데, GET/POST request를 handling하는 view를 만들었다
Route..가 아니고 urls
Rails나 다른 프레임워크를 썼던 사람들은 Django에 Route 설정이 없다는 것에 꽤 놀랄 수 있다. View와 url을 mapping하는 가장 쉬운 방법은 url mapping이다.
위에서 만들었던 LeadListCreate를 api/lead/와 묶어보자. 즉, 모델 조회와 생성시에 api/lead/로 GET/POST 요청을 날리고 싶다. URL mapping을 설정하기 위해서는 ./project/urls.py 을 연다.
from django.urls import path, include
urlpatterns = [
path('', include('leads.urls'))
]
그리고 ./leads/urls.py로 가서
from django.urls import path
from . import views
urlpatterns = [
path('api/lead/', views.LeadListCreate.as_view()),
]
이렇게 작성해준다.
마지막으로 설정의 INSTALLED_APPS 에서 아래와 같이 rest_framework를 추가해주면,
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'leads',
'rest_framework' # enable rest framework
]
python manage.py runserver
위 화면을 확인 가능하다.
DB seeding
Django fixture을 통해 DB에 데이터를 넣을 수 있다. Fixture는 frontend 데이터를 가지고 데모할 때 유용하게 쓸수 있다. ./leads/fixtures 라는 폴더를 만들고 leads.json라는 파일을 아래와 같이 작성한다.
[
{
"model": "leads.lead",
"pk": 1,
"fields": {
"name": "Armin",
"email": "something@gmail.com",
"message": "I am looking for a Javascript mentor",
"created_at": "2018-02-14 00:00:00"
}
},
{
"model": "leads.lead",
"pk": 2,
"fields": {
"name": "Tom",
"email": "tomsomething@gmail.com",
"message": "I want to talk about a Python project",
"created_at": "2018-01-14 00:00:00"
}
}
]
저장한 후 fixture를 불러온다
python manage.py loaddata leads
끝!
React Frontend
Django 와 React 를 어떻게 하면 잘 붙힐 수 있을까에 대해서는 여러가지 의견이 있다.
결과적으로는 얼마나 많은 Javascript가 필요한지에 따라 다르다. 보통은 3가지 패턴이 존재한다.
- React를 Django의 “frontend” app으로 가져간다: single HTML 템플릿을 load하고 React가 frontend를 관리하게 한다
- Django를 독립적인 Rest API로 두고, React도 독립적인 SPA로 둔다 (인증을 위해 JWT가 필요하다)
- 섞는다: 작은 React app 들을 Django 템플릿안에 넣는다
1번으로 가는 경우:
- app같은 웹사이트를 만들때
- user interaction 이 많은 인터페이스일때
- SEO 걱정이 없을때
- react router가 괜찮을때
React를 Django에 가깝게 유지하는 것은 인증이나 다른 것들을 쉽게 해준다. Django 빌트인 인증을 이용해서 사용자들의 등록이나 로그인을 만들 수 있고, 빌트인 Session 인증을 통해 토큰이나 JWT을 걱정하지 않을 수 있다.
3번으로 가는 경우:
- javascript 가 별로 필요없다
- SEO를 신경써야한다
React 설정과 웹팩
1번으로 실습을 진행한다
Django와 React 조합의 장점은 API를 제공하는 Django Rest Framework이다. React는 “frontend”라는 개별적 app 으로 만들 것이다.
이전과 마찬가지로
django-admin startapp frontend
로 app을 만들어준다.
$HOME/django-drf-react-quickstart/project/ 밑에 frontend라는 디렉토리가 생겼을것이다.
mkdir -p ./frontend/src/components
mkdir -p ./frontend/{static,templates}/frontend
cd ..
// $HOME/django-drf-react-quickstart
npm init -y
// npm 환경 초기화
npm i webpack webpack-cli --save-dev
webpack과 webpack-cli를 설치한 후 package.json에 scripts를 아래와 같이 설정한다
"scripts": {
"dev": "webpack --mode development ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js",
"build": "webpack --mode production ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js"
}
그리고 코드 트랜스파일을 위해 babel을 설치한다
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react babel-plugin-transform-class-properties --save-dev
babel-plugin-transform-class-properties 는 ES6 클래스 static property를 사용하기 위해 필수적이다.
npm i react react-dom prop-types --save-dev
React와 prop-type도 설치한다
프로젝트 폴더내에 .babelrc 라는 새로운 파일을 만들어 babel 을 설정해준다.
{
"presets": [
"@babel/preset-env", "@babel/preset-react"
],
"plugins": [
"transform-class-properties"
]
}
마지막으로 webpack.config.js라는 파일을 작성하여 babel-loader를 설정해준다
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
셋팅 끝!
React Frontend
./frontend/views.py를 작성한다.
from django.shortcuts import render
def index(request):
return render(request, 'frontend/index.html')
./frontend/templates/frontend/index.html을 작성한다
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
<title>Django DRF - React : Quickstart - Valentino G. - www.valentinog.com</title>
</head>
<body>
<section class="section">
<div class="container">
<div id="app" class="columns"><!-- React --></div>
</div>
</section>
</body>
</html>
마지막에서 frontend/main.js를 호출한다
./project/urls.py를 열고
urlpatterns = [
path('', include('leads.urls')),
path('', include('frontend.urls')),
]
frontend.urls를 포함해준다
./frontend/urls.py를 열고
from django.urls import path
from . import views
urlpatterns = [
path('', views.index ),
]
view를 연결해준다.
./project/settings.py를 열고 installed_app에 frontend를 추가해준다
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'leads',
'rest_framework',
'frontend' # enable the frontend app
]
지금 서버를 실행시켜보면
python manage.py runserver
아무것도 안뜬다. React가 없기 떄문.
React frontend를 만들기 위해서 3개의 컴포넌트가 필요한데,
- Mother Component인 App
- data fetching을 위한 stateful component인 Dataprovider
- 데이터 display를 위한 stateless component 인 Table
App
에 react를 붙히기 위한 메인 컴포넌트이다. ./frontend/src/components/App.js라는 파일을 만든다.
import React from "react";
import ReactDOM from "react-dom";
import DataProvider from "./DataProvider";
import Table from "./Table";
const App = () => (
<DataProvider endpoint="api/lead/"
render={data => <Table data={data} />} />
);
const wrapper = document.getElementById("app");
wrapper ? ReactDOM.render(<App />, wrapper) : null;