8.6 Webサーバーへのデプロイ
ここまでは、Reactのプログラムをnpm startコマンドで、node.jsの開発用サーバーを使ってプログラムを動かしていましたが、実際のシステムではバックエンドと同じWebサーバーにデプロイして動作させることになります。まずは、sessionやCSRFトークンを使ったコードを動かしテストするために、開発したReactコンポーネントをdjangoの開発サーバーにデプロイする方法を説明します。(最終的には、ApatchなどのWebサーバーソフトにデプロイすることになるのですが、それは後ほど別に説明します。)

Reactプログラムの修正
「8.5.4 メニュー選択でページを切り替える」で説明した、react-router-domに関する箇所は、見直しが必要になります。
開発環境のサーバーを使うと、Reactアプリケーションを呼び出すindex.htmlはルートディレクトリ"/"に配置されているものとして実行されます。8.5.4に記載したサンプルコードのreact-router-domに関するpathやhrefなどに設定されているパスもその前提で書いてあり、このパス以外に配置されても動きません。しかし、実際のWebサーバーでは必ずしもルートパスにReactプログラムを呼び出すindex.htmlが配置されるとは限りません。Webサーバー上のパスが変わっても動作するようにReactのプログラムを書き換えます。
以後、'my_app'というパスにReactプログラムを呼び出すindex.htmlを配置する場合、つまりhttp://127.0.0.1:8000/my_app/のURLでReactアプリケーションを呼び出すようにする場合を例に説明します。
Webサーバー上の配置パスは、Reactプロジェクトフォルダにあるpackage.jsonというファイルに設定します。次のように、homepage属性にmy_app/を設定する行を追加します。
{
"name": "frontend",
"version": "0.1.0",
"homepage": "my_app/",
"private": true,
:
homepageで設定した値は、環境変数PUBLIC_URLに設定され、Reactのプログラムからはprocess.env.PUBLIC_URLでアクセスできるようになります。
次に、Reactのプログラムの中のreact-router-domに関する場所を見直していきます。
BrowserRouter コンポーネントに basename を設定すると、その配下にある Link 、 NavLink 、Routeなどは自分が持っているパスの値に自動的に basename を補完します。例えば、
<BrowserRouter basename={”/my_app"}>
であれば、
<Route path="/page_a" element={<PageA />}
のpathは自動的に"/my_app/page_a"と解釈されるようになります。
8.5.4に掲載したサンプルプログラムMenuSamplePage.jsは、以下のようになります。
//MenuSamplePage.js
import {BrowserRouter, Routes, Route } from 'react-router-dom';
import NavigationMenu from './NavigationMenu';
import PageA from './PageA';
import PageB from './PageB';
import PageC1 from './PageC1';
import PageC2 from './PageC2';
import './MenuSamplePage.css';
import 'bootstrap/dist/css/bootstrap.min.css';
function MenuSamplePage(){
return(
<div>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<NavigationMenu />
<Routes>
<Route path="/page_a" element={<PageA />} />
<Route path="/page_b" element={<PageB />} />
<Route path="/page_c1" element={<PageC1 />} />
<Route path="/page_c2" element={<PageC2 />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default MenuSamplePage;
次に、ナビゲーションメニューなどでhrefを使っているところをreact-router-domが提供しているNavLink(もしくはLink)に置き換えます。NavLinkを使えば、パスの値に自動的に basename で設定した値を補完してくれます。React bootstrapのコンポーネントNav.LinkやNavDropdown.ItemでNavLinkを使うためには、以下のように as={NavLink} を指定し、href の代わりに to プロパティを使用します。
<Nav.Link as={NavLink} to="/page_a">ページA</Nav.Link>
<NavDropdown.Item as={NavLink} to="/page_c1">ページ C-1</NavDropdown.Item>
8.5.4に掲載したサンプルプログラムNavigationMenu.jsは、以下のようになります。
//NavigationMenu.js
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import Button from 'react-bootstrap/Button';
import { NavLink } from 'react-router-dom';
function NavigationMenu(props){
return(
<Navbar expand="lg" className="bg-body-tertiary" data-bs-theme="dark">
<Container>
<Navbar.Brand href="/page_a">Sample</Navbar.Brand> {/* 製品名、ロゴなどを表示*/}
<Navbar.Toggle aria-controls="basic-navbar-nav" /> {/* 一定幅を超えるとハンバーガーメニューを表示 */}
<Navbar.Collapse id="basic-navbar-nav"> {/* 一定幅を超えると子要素を非表示に */}
<Nav className="mr-auto">
<Nav.Link as={NavLink} to="/page_a">ページA</Nav.Link>
<Nav.Link as={NavLink} to="/page_b">ページB</Nav.Link>
{/*<Nav.Link href={"/page_a"}>ページA</Nav.Link>
<Nav.Link href={"/page_b"}>ページB</Nav.Link>*/}
<NavDropdown title="ページC" id="activities-dropdown">
<NavDropdown.Item as={NavLink} to="/page_c1">ページ C-1</NavDropdown.Item>
<NavDropdown.Item as={NavLink} to="/page_c2">ページ C-2</NavDropdown.Item>
{/*<NavDropdown.Item href={"/page_c1"}>ページ C-1</NavDropdown.Item>
<NavDropdown.Item href={"/page_c2"}>ページ C-2</NavDropdown.Item> */}
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
);
}
export default NavigationMenu;
Djangoの設定
次にdjangoの開発用サーバーの設定を行います。
djangoのサーバーに、画像、JavaScript、CSS などの静的ファイルを配置してこれを呼び出すことができます。Reactアプリのファイルをサーバーに配置するために、settings.pyに以下を設定します。
- STATIC_URL : 静的ファイルを呼び出すためのURI
- STATIC_DIRS:静的ファイルを配置するディレクトリ
次のように定義した場合、{ベースディレクトリ}/frontendの下にindex.htmlが置いてあれば、http://127.0.0.1:8000/my_app/index.html でアクセスできます。
#settings.py
STATIC_URL = 'my_app/'
STATICFILES_DIRS = (
BASE_DIR.joinpath('frontend'),
)
Reactアプリケーションのビルドとデプロイ
(1) プロダクトビルド
ここまでReactの開発では、npm start を実行してローカルでアプリケーションを実行していました。ソースコードの変更が動的にビルドされるため、ローカルで起動中のアプリケーションにリアルタイムに反映することができます。このビルド方法をDevelopment Build(開発ビルド)と言います。
これに対して、本番アプリケーションとしてのデプロイのために使用されるビルド方法を Production Build(本番ビルド)といいます。Production Buildでは、Development Buildに比べてパフォーマンスやその他本番利用の面での最適化(Optimize)がされたビルドが行われます。
npm run build
を実行すると、buildというフォルダができ、この中にWebサーバーにデプロイするモジュールができあがります。モジュールの中には次のようなファイルやディレクトリが含まれています。
- index.html : Webサーバーから呼び出されるエントリーポイント
- static/js : JavaScriptで書かれたコンポーネントや使っている第三者モジュールを圧縮・結合したファイルが置かれるディレクトリ
- static/css : アプリケーションやBootstrapなどのCSSファイルを圧縮・結合したファイルが置かれるディレクトリ
- static/media : それ以外の静的アセット(イメージ、フォントなど)を圧縮・結合したファイルが置かれるディレクトリ
staticの下に入っているのが、バンドルされたReactのプログラムです。Reactはコンポーネントやライブラリごとにファイルをいくつも分割して作成しますが、分割されたままのファイルをブラウザに読み込ませると、ファイル数だけ通信が発生し、ページの表示速度が極端に遅くなってしまいます。そこで、これらを圧縮・結合してブラウザからの読み込みを最適化するようにしています。
バンドルされたプログラムはindex.htmlから呼び出されます。以下は自動的に生成されたindex.htmlの例です。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="/my_app/favicon.ico"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Web site created using create-react-app"/>
<link rel="apple-touch-icon" href="/my_app/logo192.png"/>
<link rel="manifest" href="/my_app/manifest.json"/>
<title>React App</title>
<script defer="defer" src="/my_app/static/js/main.5d2c6aac.js"></script>
<link href="/my_app/static/css/main.3d02da29.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
アプリケーションが含まれるJavaScriptは以下の行で呼び出されています。
<script defer="defer" src="/my_app/static/js/main.5d2c6aac.js"></script>
ここでsrcの先頭が"/my_app"になっているのがわかります。package.jsonの"homepage"での設定がここに反映されています。
(2) デプロイ
プロダクションビルドで生成されたbuildディレクトリを、djangoのプロジェクトディレクトリ下にfrontendという名前にしてコピーします。
cp -r {Reactプロジェクトディレクトリ}/build {djangoプロジェクトディレクトリ}/frontend
この状態で、以下のURLでdjango開発用サーバーにアクセスするとReactのプログラムが起動されます。
http://127.0.0.1:8000/my_app/index.html