Reactで作成した画面からExpressで作成したAPIを呼び、取得したデータを画面表示するところまでを構築していきます。
React/Expressはそれぞれ別のDockerコンテナで動作させます。
ディレクトリ
app/
├── docker/
│ ├── docker-compose.yml
│ └── Dockerfile
├── backend/
└── frontend/
docker/
の2ファイルはホスト側で作成しますが、その他の backend/
と frontend/
内のファイルは全てコンテナ側で作成していきます。
Docker
Dockerfile
と docker-compose.yml
を作成し、コンテナを起動します。
ファイル作成
React、Express で共通で使う Dockerfile を1つ作成します。
FROM node:14
今回はひとまずnodeが動けば良いので何も設定していません。
次に docker-compose.yml を作成します。
version: '3'
networks:
react_express:
driver: bridge
services:
frontend: # React用のコンテナ
build: .
container_name: front
tty: true
volumes:
- ../frontend:/frontend
working_dir: "/frontend"
ports:
- 3001:3000
networks:
- react_express
backend: # Express用のコンテナ
build: .
container_name: back
tty: true
volumes:
- ../backend:/backend
working_dir: "/backend"
ports:
- 3002:3000
networks:
- react_express
-
network
: 2つのコンテナを同じネットワーク内に配置します。 -
volumes
: 開発の便宜上、ホストとコンテナでファイル共有しています。 -
ports
: frontend、backendの各サーバ用のポート 3000 を、それぞれホストのポート 3001、3002 に転送しています。
コンテナの起動
コンテナを起動します。
$ cd docker/
$ docker-compose up -d
...
$ docker-compose ps
back docker-entrypoint.sh node Up 0.0.0.0:3002->3000/tcp,:::3002->3000/tcp
front docker-entrypoint.sh node Up 0.0.0.0:3001->3000/tcp,:::3001->3000/tcp
Up
となっていればOKです。
Express (Backend)
プロジェクト作成
docker exec
を使って、Express用のコンテナ (back) に入ります。
$ docker exec -u node -it back /bin/bash
package.json を作成します。
$ npm init -y
Expressをインストールします。
$ npm install express
APIの作成
index.js を作成します。
const express = require('express');
const app = express();
const port = 3000;
app.get('/user', (req, res) => {
res.json([{
id: 1,
name: "Taro"
}, {
id: 2,
name: "Jiro"
}]);
});
app.listen(port, () => console.log(`Server running on port ${port}`));
/user
にリクエストが来たときに、jsonデータを返すようにしています。
Expressサーバの起動
準備ができたので、Expressサーバを起動します。
$ node index.js
Server running on port 3000
React (Frontend)
プロジェクト作成
別のターミナルを立ち上げ、docker exec
を使って、React用のコンテナ (front) に入ります。
$ docker exec -u node -it front /bin/bash
create-react-app
コマンドを使って、プロジェクトを作成します。
(ドットを付けることで、ディレクトリ直下に作成できます)
$ npx create-react-app .
Proxyの設定
Dockerのネットワーク環境における Front/Backend の各オリジン ( http://xxx:yy
)は、
- Frontend :
http://frontend:3000
- Backend :
http://backend:3000
のように異なるため、CORS (Cross-Origin Resource Sharing) の設定をする必要があります。
ここでは、Front側に Proxy を使う方法で回避します。以下2パターンのどちらかを使います。
方法1: package.json に proxy を設定する
package.json に以下を追記します。
"proxy": "http://backend:3000",
この設定があると、text/html 以外のヘッダのリクエストを proxy のアドレスを origin としてリクエストするようになるため、Same-Origin として認識されます。
方法2: http-proxy-middleware を使う
複数の proxy を設定したい場合にはこちらの方法を使います。
src/setupProxy.js
を作成します。
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy('/user', { target: 'http://backend:3000' }))
}
この設定により、Reactアプリに対して /user
にリクエストが来たときに http://backend:3000
にリクエストが飛ぶことになり、方法1と同様に Same-Origin として通信できます。
app.use(proxy())
は複数記述できるため、リクエストのパスにより別の proxy に分けて設定することもできます。
ちなみに create-react-app
は、npm start
の実行時に setupProxy.js
を自動で読み込んで処理をするようになっているため、他のファイルにimportをする必要はありません。(ただし、npm run build
では機能しません。)
画面の作成
src/App.js
を以下に編集します。
import { useEffect, useState } from 'react';
const App = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/user')
.then(res => res.json())
.then(data => {
setUsers(data);
}).catch(err => {
console.log(err)
});
}, []);
return (
<div>
{users.map((user, index) =>
<p key={index}>{ user.name }</p>
)}
</div>
);
}
export default App;
画面読み込み時に fetch('/user')
が実行され、Proxyの設定によりExpressサーバ ( http://backend:3000/user
) にリクエストが送られます。
このAPIから取得したデータを users
ステートに格納し、画面に表示します。
Reactサーバの起動
Reactサーバを起動します。
$ yarn start
ホスト側のブラウザで http://localhost:3001/
にアクセスすると、
Taro
Jiro
が画面に表示されることが確認できました。