이곳에 쓰인 모든 글은 http://v1k45.com/blog 의 http://v1k45.com/blog/modern-django-part-1-setting-up-django-and-react/ 에 쓰여진 대로 진행함을 알립니다.
또한 이 글의 원본은 https://killi8n.com 에 있음을 알립니다! (블로그 홍보는 안
비밀...)
이번에 올릴 글은 장고와 리액트로 dev 모드와 production 모드로 화면 띄워보기 입니다. 일단 구조는 Root 앱 밑에 Frontend(React) , Backend(Django) 두 폴더가 있는 구조 입니다.
mkdir Deact
# make react project directory from cra
create-react-app deact-frontend
# django project
mkdir deact-backend
cd deact-backend
# make virtual env
virtualenv --python=python3 venv
# activate virtual env
source venv/bin/activate
# install django
pip install django
django-admin startproject deact
cd deact
python manage.py migrate
python manage.py runserver
자 그리고 이제 react directory로 와서 eject를 시켜주세요.
yarn eject
다시 django 디렉토리로 와서 다음 명령어로 인스톨 해줍니다.
pip install django-webpack-loader
그 다음에 settings.py에서 INSTALLED_APPS 에 'webpack_loader'를 추가해줍니다. 그리고 webpack_loader에 관한 설정과 저희의 리액트 앱을 보여줄 template에 관한 설정도 추가해줍니다.
settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'webpack_loader',
]
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates"),],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
...
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.dev.json'),
}
}
그리고 templates 디렉토리를 장고 프로젝트의 Root디렉토리 밑에 만든후, index.html을 추가합니다.
templates/index.html
{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Deact</title>
</head>
<body>
<div id="root">
</div>
{% render_bundle 'main' %}
</body>
</html>
그리고 urls.py에 해당 템플릿 뷰를 라우팅 해줍니다.
urls.py
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('', TemplateView.as_view(template_name="index.html")),
]
이제 localhost:8000 으로 접속하면, 다음과 같이 오류가 날것입니다!
리액트 프로젝트에서 webpack loader설정을 안해서 나는 당연한 오류입니다.
이제 리액트로 넘어와서 다시 설정을 해줘야 합니다.
# webpack bundle tracker 설치
yarn add --dev webpack-bundle-tracker
config/path.js
module.exports = {
dotenv: resolveApp(".env"),
appBuild: resolveApp("build"),
appPublic: resolveApp("public"),
appHtml: resolveApp("public/index.html"),
appIndexJs: resolveApp("src/index.js"),
appPackageJson: resolveApp("package.json"),
appSrc: resolveApp("src"),
yarnLockFile: resolveApp("yarn.lock"),
testsSetup: resolveApp("src/setupTests.js"),
appNodeModules: resolveApp("node_modules"),
publicUrl: getPublicUrl(resolveApp("package.json")),
servedPath: getServedPath(resolveApp("package.json")),
// 추가 해줍니다.
statsRoot: resolveApp("../deact-backend/deact")
};
config/webpack.config.dev.js
// bundleTracker를 import 해 줍니다.
const BundleTracker = require('webpack-bundle-tracker');
// publicPath와 publicUrl을 다음과 같이 변경해줍니다.
const publicPath = 'http://localhost:3000/';
const publicUrl = 'http://localhost:3000/';
// entry 부분을 아래와 같이 변경해줍니다.
entry: [
require.resolve("./polyfills"),
require.resolve("webpack-dev-server/client") + "?http://localhost:3000",
require.resolve("webpack/hot/dev-server"),
require.resolve("react-dev-utils/webpackHotDevClient"),
paths.appIndexJs
],
// 가장 아래의 plugins 쪽에 다음을 추가해줍니다.
plugins: [
...
new BundleTracker({path: paths.statsRoot, filename: 'webpack-stats.dev.json'}),
]
config/webpackDevServer.config.js
// host 밑에 다음을 추가해줍니다.
host: host,
headers: {
'Access-Control-Allow-Origin': '*'
},
자 이제 다시 yarn start로 시작 한후, localhost:8000으로 접속하면 첫 화면이 뜰것입니다.
여기까지 Dev모드의 React를 띄우는 것을 알아 보았는데요, 그럼 이번에는 build된 React를 띄우는 Prod 모드를 알아봅시다.
루트 프로젝트 아래의 deact 앱 아래에 production_settings.py를 다음과 같이 생성해줍니다.
deact/deact/production_settings.py
from .settings import *
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "assets"),
]
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.prod.json'),
}
}
그 다음에 templates 디렉토리와 같은 위치에 assets/bundles 디렉토리를 차례로 생성해줍니다. 나중에 리액트앱이 빌드 될때 파일이 이곳으로 모이게 됩니다.
그리고 다시 리액트의 config/paths.js 에서 appBuild를 아까 만든 assets/bundles 경로로 설정해줍니다.
config/paths.js
module.exports = {
dotenv: resolveApp(".env"),
// 변경 할 부분입니다.
appBuild: resolveApp("../deact-backend/deact/assets/bundles"),
appPublic: resolveApp("public"),
appHtml: resolveApp("public/index.html"),
appIndexJs: resolveApp("src/index.js"),
appPackageJson: resolveApp("package.json"),
appSrc: resolveApp("src"),
yarnLockFile: resolveApp("yarn.lock"),
testsSetup: resolveApp("src/setupTests.js"),
appNodeModules: resolveApp("node_modules"),
publicUrl: getPublicUrl(resolveApp("package.json")),
servedPath: getServedPath(resolveApp("package.json")),
statsRoot: resolveApp("../deact-backend/deact")
};
그리고 config/webpack.config.prod.js 에서 다음과 같은 작업들을 해야합니다.
config/webpack.config.prod.js
// Bundle Tracker를 import해줍니다(상단)
const BundleTracker = require('webpack-bundle-tracker');
// 기존의 publicPath를 다음과 같이 바꿔줍니다.
const publicPath = "/static/bundles/";
// 기존의 cssFilename을 다음과 같이 변경해줍니다. (앞의 static/ 을 없애줍니다.)
const cssFilename = 'css/[name].[contenthash:8].css';
// 약 67 번째 라인에 있는 경로도 모두 static을 제거 해줍니다.
...
output: {
...
filename: "js/[name].[chunkhash:8].js",
chunkFilename: "js/[name].[chunkhash:8].chunk.js",
...
}
// 약 140번째 라인의 경로도 static을 제거해줍니다.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "media/[name].[hash:8].[ext]"
}
},
// 약 223번째 라인의 경로도 static을 제거해줍니다.
options: {
name: "media/[name].[hash:8].[ext]"
}
// 마지막 plugins 에 다음을 추가해줍니다.
plugins: [
...
new BundleTracker({
path: paths.statsRoot,
filename: "webpack-stats.prod.json"
})
...
]
...
쉽게 설명을 해드리면 ctrl + f 로 static을 검색하신후, 경로로 잡히는 모든것을 삭제해 주시면 됩니다. 그 후 plugins 에 bundleTracker관련 코드를 추가해 줍니다.
그리고 리액트 쪽에서 build를 해줍니다.
yarn build
그 후 장고 쪽에서 다음과 같은 명령어로 production 모드를 띄워줍니다.
python manage.py runserver --settings=deact.production_settings
잘 뜨나요?
여기 까지 되셨다면 production도 띄울수 있게 된것입니다!
안녕하세요~ 올려주신 방식대로 따라해보고있는데요~ ㅠㅠ 자꾸 yarn start에서 ./polyfills 를 찾을 수 없다고 하는데 어떤 문제가 있는건지 혹시 알 수 있을까요? webpack.config.dev.js에 entry: [ require.resolve("./polyfills"), 이부분입니다...
좋은 포스팅 정말 감사합니다!
한가지 검토가 필요한 부분이 있는데요.
CRA를 하고 난 이후에 yarn eject를 하고 나면
config 폴더에는 webpack.config.js 만 있더라구요.
그래서 저는 복사해서 webpack.config.prod.js 를 만들어서 수정했던 것인데
이 후 빌드를 하게 될 때마다 build 스크립트에 명시된
const configFactory = require('../config/webpack.config');
이 부분 때문에 prod 파일이 참조가 되지 않는 것 같아보여요.
포스팅의 맨 마지막 화면 띄우는 부분에서 버벅이다가
const configFactory = require('../config/webpack.config.prod');
로 바꿔주고 빌드를 다시하고나니 페이지가 호출되는 것 같습니다.
통합하기 이후 webpack loader 설정중인데...
django 설정도 모두 잘되어있고 front yarn eject와 webpack-bundle-tracker, config쪽 설정도 모두 완료했습니다.
OSError: Error reading /Users/donghyunkwon/development/project/ponynote/webpack-stats.dev.json. Are you sure webpack has generated the file and the path is correct?
webpack-stats.dev.json 이거를 frontend로 못가져가는것 같은데... 프로젝트만 3~4번 새로 해도 도저히 안되서 글을 올립니다 ㅠㅠ
yarn eject 실행시 config에는 webpack.config.js와 webpackDevServer.config.js 이렇게 추가되었습니다.
감사합니다 :)
라프텔에서도 리액트랑 장고랑 함께 쓰고 있는데,
이제는 서버사이드 렌더링을 하게 되면서 리액트 관련 프로젝트랑 장고서버랑은 완전히 선이 그어진 상태로 분리되어있어요.
장고에선 API 만 관리하고..
나머지는 Node 로 만든 섭사렌 서버에서 관리하는거죠 ㅎㅎ