🐷

フロントReact、サーバーNode(Express)、MySQLで開発をした話

2022/03/10に公開

先日開発を行った際のプロジェクト構成を、備忘録として残しておきます。

開発環境

  • MacOS 12.2 M1 Processer (今回の環境はx64でも動作するようにしています)
  • Docker

行いたかったこと

React&Nodeサーバー&MySQLコンテナを同時に起動させ、ReactとNodeコンテナをプロキシ経由で接続させて開発を行いました。
そして最終的に、EC2にデプロイさせるため、buildしたファイルをExpressサーバー上で動かすというところまで行いました。

まずは開発環境の構成から

フォルダ構成

今回のプロジェクト構成は以下になっております。
(一部抜粋)

.
├── backend
│   ├── app
│   │   ├── app.js
│   │   ├── .dockerignore  //これ重要
│   │   ├── node_modules
│   │   ├── package.json
│   │   └── yarn.lock
│   └── dockerfile
│       └── Dockerfile
├── database
│   └── mysql
│       ├── dockerfile
│       │   └── Dockerfile
│       ├── my.cnf
│       └── sql
│           └── create_db.sql
├── docker-compose.yml
└── frontend
    ├── app
    │   ├── .dockerignore  //これ重要
    │   ├── node_modules
    │   ├── package.json
    │   ├── public
    │   ├── src
    │   ├── tsconfig.json
    │   └── yarn.lock
    └── dockerfile
        └── Dockerfile

docker-compose.ymlの構成

docker-compose.yml
version: "3"

services:
  express:
    build: ./backend/dockerfile  # Dockerfileの親フォルダパス
    container_name: express-container  # コンテナ名
    hostname: express-server  # ホスト名
    tty: true
    environment:
      - NODE_ENV=DEVELOPMENT
      - PORT=3001  # コンテナ内でのポート番号
    volumes:
      - ./backend/app:/usr/src/app
      - express-data:/usr/src/app/node_modules # volumeのエントリポイント => 高速化
    ports:
      - 3001:3001 # 公開ポート番号:environmentで指定したポート番号
    working_dir: /usr/src/app
    command: sh -c "yarn install && yarn global add nodemon && nodemon app.js"

  react:
    build: ./frontend/dockerfile  # Dockerfileの親フォルダパス
    container_name: react-container  # コンテナ名
    hostname: react-server  # ホスト名
    tty: true
    environment:
      - NODE_ENV=DEVELOPMENT
      - PORT=3000  # コンテナ内でのポート番号
    volumes:
      - ./frontend/app:/usr/src/app
      - react-data:/usr/src/app/node_modules  # volumeのエントリポイント => 高速化
    ports:
      - 3000:3000 # 公開ポート番号:environmentで指定したポート番号
    working_dir: /usr/src/app
    command: sh -c "yarn install && yarn start"

  mysql:
    build: ./database/mysql/dockerfile  # Dockerfileの親フォルダパス
    container_name: mysql-container  # コンテナ名
    hostname: mysql-server  # ホスト名
    platform: linux/x86_64
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - mysql-data:/var/lib/mysql  # volumeのエントリポイント
      - ./database/mysql/my.cnf:/etc/mysql/conf.d/my.cnf  # cnfファイル
      - ./database/mysql/sql/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql # 起動時に実行するsqlファイル
    environment:
      MYSQL_ROOT_PASSWORD: password  # rootユーザーのパスワード
      TZ: Japan
    ports:
      - 3306:3306  # 公開ポート番号:3306(デフォルト値)
    restart: always

volumes:
  mysql-data:
    name: db-data
  express-data:
    name: express-data
  react-data:
    name: react-data

コメントで書いてある通り、node_module用のvolumeを作成しています。
これを作成しているかいないかで、読み込み速度が全然違います!
node_module用のvolumeは必ず作成しておきましょう。

DockerFileの構成

React&Express

Dockerfile
FROM node:16.13.2-alpine3.15

WORKDIR /usr/src/app

MySQL

今回はMySQL5.7を使用しています。
当初8を使用していましたが、Nodeのライブラリと相性が合わなかったので。。。

Dockerfile
# FROM mysql:latest
FROM mysql:5.7

RUN apt-get update -y && \
    apt-get install -y locales && \
    echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen && \
    update-locale LANG=ja_JP.UTF-8 

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL=ja_JP.UTF-8

CMD ["mysqld", "--character-set-server=utf8", "--collation-server=utf8_unicode_ci"]

主に日本語設定を記述しています。

cnfファイルの内容は以下の通り。

my.cnf
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
default-time-zone = 'Asia/Tokyo'
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqldump]
default-character-set=utf8

同じような記述が多いので、もしかしたら必要ない箇所があるかもです。

.dockerignoreでnode_modulesを指定

.dockerignore
node_modules/

この一行だけです。
ReactとExpressのプロジェクト配下に作成しましょう。

ReactからExpressに接続するためにプロキシを構成。

Reactプロジェクト内のpackage.jsonに以下を追記します。

package.json
{
  ...
  "proxy": "http://express-container:3001",
  ...
}

先ほどdocker-compose.ymlに記載したコンテナ名:ポート番号です。

AxiosなどでExpressにリクエストをする際は、

axios.get(`/api/...`)

の用に記述すればOK!

デプロイ時の構成

主な変更点は次のとおりです。

  • Reactコンテナの排除
  • Expressのポート番号変更

フォルダ構成

.
├── backend
│   ├── app
│   │   ├── app.js
│   │   ├── build  // Reactのビルドデータ
│   │   ├── .dockerignore  //これ重要
│   │   ├── node_modules
│   │   ├── package.json
│   │   └── yarn.lock
│   └── dockerfile
│       └── Dockerfile
├── database
│   └── mysql
│       ├── dockerfile
│       │   └── Dockerfile
│       ├── my.cnf
│       └── sql
│           └── create_db.sql
└── docker-compose.yml

docker-compose.ymlの構成

docker-compose.yml
version: "3"

services:
  express:
    build: ./backend/dockerfile  # Dockerfileの親フォルダパス
    container_name: express-container  # コンテナ名
    hostname: express-server  # ホスト名
    tty: true
    environment:
      - NODE_ENV=PRODUCTION # 本番用設定
      - PORT=3001  # コンテナ内でのポート番号
    volumes:
      - ./backend/app:/usr/src/app
      - express-data:/usr/src/app/node_modules # volumeのエントリポイント => 高速化
    ports:
      - 80:3001 # 公開ポート番号:environmentで指定したポート番号
    working_dir: /usr/src/app
    command: sh -c "yarn install && node app.js"  # 本番ではnodenv不要。

  mysql:
    build: ./database/mysql/dockerfile  # Dockerfileの親フォルダパス
    container_name: mysql-container  # コンテナ名
    hostname: mysql-server  # ホスト名
    platform: linux/x86_64
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - mysql-data:/var/lib/mysql  # volumeのエントリポイント
      - ./database/mysql/my.cnf:/etc/mysql/conf.d/my.cnf  # cnfファイル
      - ./database/mysql/sql/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql # 起動時に実行するsqlファイル
    environment:
      MYSQL_ROOT_PASSWORD: password  # rootユーザーのパスワード
      TZ: Japan
    ports:
      - 3306:3306  # 公開ポート番号:3306(デフォルト値)
    restart: always

volumes:
  mysql-data:
    name: db-data
  express-data:
    name: express-data

expressコンテナのポート番号を3001から80へ変更しています。

Expressのルーティング構成

私は今回以下のようなルーティングを行いました。

app.js
const express = require("express");
const app = express();
const router = require("express").Router();

...

// Dynamic resources rooting.
app.use("/api", router);
router.use("/user", require("./routes/user.js"));
/*
ルーティングをいくつか記述。
ルーティング処理を全て記述した最後に以下を記述。
*/
app.get("*", (req, res) => {
  res.sendFile(path.join(__dirname, ".", "build", "index.html"));
});

このように処理を書いておくことで、/api以外のリクエストは全てindex.htmlに流れるようになっています。
(404ページも含む)

EC2にデプロイして、インスタンスのURLにアクセスしてみよう!

Reactで作成したトップページが表示されればOKです。
私の場合、初回はログインページへ遷移するようにしていましたが、正常に動作しているようです!

/hogeでアクセスすると、

正常にNotFoundページも表示されました!

GitHubで編集を提案

Discussion