2022-06-18 投稿

Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【前編:サーバーサイド】

Webアプリ開発に慣れない人にとって環境構築は大きなハードルです。 この記事ではサーバーサイドはDockerとLaravel、フロントエンドではReactとTypeScriptを使ったフルスタックパッケージを、ひとつづつ手順を確認しながら構築していきます。 前編ではサーバーサイドのDockerとLaravelの構築までを行います。

構成の確認

  1. Docker(Nginx+PHP+MySQL)
  2. Laravel(Composer)
  3. React
  4. TypeScript

GitHubで手順ごとにソースを公開しています。

git clone https://github.com/mariebell/fullstack-project.git

1. Docker環境構築

仮想環境上にアプリケーションを構築します。Dockerの環境構築から始めましょう。

Docker+Laravel環境構築(前編での最終形)

1fullstack-project/
2  ├ src/
3  |   └ (Laravelのコード)
4  ├ docker/
5  │   ├ nginx/
6  │   │   ├ Dockerfile
7  │   │   └ default.conf
8  │   ├ php/
9  │   │   ├ Dockerfile
10  │   │   └ php.ini
11  │   └ mysql/
12  │       ├ Dockerfile
13  │       └ my.conf
14  └ docker-compose.yml

Docker Desktopをインストールし、起動していることを確認したら、まずは新しく作ったディレクトリの中にdocker-compose.ymlを作成します。

1mkdir fullstack-project
2cd ./fullstack-project
3touch docker-compose.yml

1-1. Nginx

まずはHTTPリクエストを捌くリバースプロキシ用のNginxを準備します。

docker-compose.yml

Docker Compose

アプリ開発では複数のコンテナを運用するのが一般的なので、まずdocker-compose.ymlから編集していきます。

1version: “3.9”
2services:
3  (これから記述していきます)

docker-compose.ymlファイルを新規作成し、まずはversionとservicesを記述します。

versionは最新版の3.9としていますが、3と書いても大丈夫です。 servicesの中に動かしたいサービス名を書きます。

ここではNginxコンテナを動かすサービスをproxyとします。

1version: “3.9”
2services:
3  proxy:
4    container_name: fullstack-proxy
5    build: ./docker/nginx
6    ports:
7      - “8000:80"
8    volumes:
9      - ./src:/usr/share/nginx/html

container_name: (コンテナ名)
docker psコマンドで起動中のコンテナを確認する際に便利なのでcontainer_nameを指定します。

build: (Dockerfileがあるディレクトリ)
buildにDockerfileがあるディレクトリを指定します。 DockerHubにあるDockerイメージをそのまま利用する場合は、build: ディレクトリ名の代わりにimage: イメージ名とします。

ports: "(ホストのポート番号):(ゲストのポート番号)"
ports:に、解放するローカルマシン(ホスト)のポート番号と仮想コンテナ(ゲスト)のポート番号を指定します。 ホスト側のポート番号を8000とすると、ブラウザからlocalhost:8000でアクセスできます。 ウェルノウンポートや他のコンテナやアプリケーションと被らない値を設定します。 ゲスト側のポート番号はHTTPのデフォルトである80で問題ありません。

volumes: "(ホストのディレクトリ):(ゲストのディレクトリ)"
volumesに、ローカルマシン(ホスト)のディレクトリと仮想コンテナ(ゲスト)のディレクトリを同期(マウント)することができます。 ホストのディレクトリで指定しておいた場所にあるファイルを編集すると、自動的にコンテナのファイルも上書きされます。
ゲスト側では、nginxのデフォルトWebルートディレクトリである/usr/share/nginx/htmlを指定しています。

docker/nginx/Dockerfile

docker-compose.ymlのbuild:で指定したDockerfileの中身を記述します。

1FROM nginx:1.21.1-alpine

FROM (ベースとなるイメージ名:タグ)
ベースとなるDockerイメージを指定します。タグによってバージョンを指定することができます。 ここで指定するイメージ名はDockerHub上で探すことができます。

nginx Tags - Docker Hub

ここでは「nginx」イメージの「1.21.1-alpine」タグがついたイメージを指定しています。 常に最新版にしたい場合はnginx:latestでも良いでしょう。

タグ末尾についているalpineとはAlpine Linuxのことで、軽量なLinuxディストリビューションイメージがくっついています。 そのためDockerコンテナをLinuxサーバライクに扱うことができます。Alpine LinuxはUbuntuよりもかなり軽量なため、Dockerイメージのサイズを抑えることができます。

default.conf

以下はnginx(1.21.1)にデフォルトで入っているdefault.confを引っ張ってきたものです。 必要に応じて編集できるようにしておきます。

1server {
2    listen       80;
3    listen  [::]:80;
4    server_name  localhost;
5
6    #access_log  /var/log/nginx/host.access.log  main;
7
8    location / {
9        root   /usr/share/nginx/html;
10        index  index.html index.htm;
11    }
12
13    #error_page  404              /404.html;
14
15    # redirect server error pages to the static page /50x.html
16    #
17    error_page   500 502 503 504  /50x.html;
18    location = /50x.html {
19        root   /usr/share/nginx/html;
20    }
21
22    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
23    #
24    #location ~ \\.php$ {
25    #    proxy_pass   http://127.0.0.1;
26    #}
27
28    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
29    #
30    #location ~ \\.php$ {
31    #    root           html;
32    #    fastcgi_pass   127.0.0.1:9000;
33    #    fastcgi_index  index.php;
34    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
35    #    include        fastcgi_params;
36    #}
37
38    # deny access to .htaccess files, if Apache's document root
39    # concurs with nginx's one
40    #
41    #location ~ /\\.ht {
42    #    deny  all;
43    #}
44}

docker-compose.ymlで指定したports:volumes:のゲスト側の値は、 default.confの2行目のポート番号(80)と、9行目のrootのパス(/usr/share/nginx/html)にそれぞれ対応しています。

確認用のHTMLを配置

volumes:のホスト側に指定したsrcディレクトリを作成し、確認用のHTML(index.html)を配置します。

1<!DOCTYPE html>
2<html lang="ja">
3<head>
4    <meta charset="UTF-8">
5    <title>Hello</title>
6</head>
7<body>
8    Hello, World.
9</body>
10</html>

docker-compose.ymlのvolumesに1行追加し、ローカルマシンのdefault.confを仮想コンテナ(ホスト)のdefault.confと同期させます。

1services:
2  proxy:
3    container_name: fullstack-proxy
4    build: ./docker/nginx
5    ports:
6      - “8000:80"
7    volumes:
8      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
9      - ./src:/usr/share/nginx/html

Dockerコンテナを起動して確認してみる

プロジェクトルートにいる状態でdocker-compose up -dコマンドで起動してみましょう。

1docker-compose up -d
2Creating network "fullstack-project_default" with the default driver
3Building proxy
4[+] Building 8.4s (6/6) FINISHED 
5
6...省略...
7
8Creating fullstack-proxy ... done
1docker ps
2CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS          PORTS                  NAMES
3e5045092c12e   fullstack-project_proxy   "/docker-entrypoint.…"   21 minutes ago   Up 21 minutes   0.0.0.0:8000->80/tcp   fullstack-proxy

ブラウザでlocalhost:8000にアクセスし、「Hello, World.」が表示されれば成功です。

fig1

ここでローカルマシンのsrc/index.htmlを編集して保存し、ブラウザをリロードすると変更が反映されることがわかります。

fig2

docker-compose downコマンドで、コンテナを停止+削除することができます。

1docker-compose down
2Stopping fullstack-proxy ... done
3Removing fullstack-proxy ... done
4Removing network fullstack-project_default

ここまでのコード(GitHub)

1-2. PHP

次にLaravelのアプリケーションを動かすために、PHPのDockerイメージを導入していきます。

docker-comopse.yml

services:に新しくLaravelを動かすためのサービスappを追加します。

1version: “3.9”
2services:
3  proxy:
4    container_name: fullstack-proxy
5    build: ./docker/nginx
6    ports:
7      - “8000:80"
8    volumes:
9      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
10      - ./src:/usr/share/nginx/html
11  app:
12    container_name: fullstack-app
13    build: ./docker/php
14    volumes:
15      - ./src:/var/www/html

container_name,build,volumesをnginxの時と同様に指定します。
ゲスト上のnginx(proxy)からゲストのphp(app)にリクエストを転送するので、portsを指定する(ローカルマシンとphpコンテナのポートを紐づける)必要はありません。

docker/php/Dockerfile

1FROM php:8.1-fpm-alpine

php:8.1-fpm-alpinePHPのバージョンは8.1で、サーバー機能を持たせるfpmと、Alpine Linuxのバージョン11であるalpineをタグに指定します。

default.confの編集(phpへのリクエストを転送)

nginxが受けたリクエストのうち、拡張子がphpのファイルをapp(phpのコンテナ)に転送する設定を行います。

location ~ \.php$ディレクティブのコメントアウトを外し、値を編集します。

1...
2    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
3    #
4    location ~ \\.php$ {
5        root           /var/www/html;
6        fastcgi_pass   app:9000;
7        fastcgi_index  index.php;
8        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
9        include        fastcgi_params;
10    }
11...}

root (phpコンテナ内のドキュメントルート);
htmlとなっている値を/var/www/htmlに変更します。

fastcgi_pass (docker-compose.ymlのサービス名:ポート番号);
Dockerで運用している場合は、fastcgi_passにIPアドレスではなくサービス名(app)を指定することができます。

fastcgi_param SCRIPT_FILENAME (phpコンテナ内のファイルパス);
/scripts$fastcgi_script_nameとなっている値を$document_root$fastcgi_script_nameに変更します。

PHP FastCGI Example

php.ini

docker cpコマンドを利用して、php-fpmにデフォルトで入っているphp.iniを引っ張ってきて、ローカルマシンで編集できるようにします。

docker cp fullstack-app:/usr/local/etc/php/php.ini-development ./docker/php/php.ini

1services:
2  ...
3  app:
4    container_name: fullstack-app
5    build: ./docker/php
6    volumes:
7      - ./docker/php/php.ini:/usr/local/php/php.ini
8      - ./src:/var/www/html

確認用のPHPを配置

/srcディレクトリに確認用のPHP(info.php)を配置します。

1<?php
2phpinfo();

Dockerコンテナを再起動して確認してみる

Dockerfileやdefault.confを編集したのでコンテナの再起動が必要です。

docker-compose restartコマンドで再起動してみましょう。

1docker-compose restart
2Restarting fullstack-proxy ... done
3Restarting fullstack-app   ... done

localhost:8000/info.phpにアクセスしてphpinfoが表示されたら成功です。

fig1 ここまででPHPが動く環境が用意できました。

ここまでのコード(GitHub)

1-3. MySQL

次に、Laravelのアプリケーションを動かすための、MySQLのDockerイメージを導入していきます。

docker-compose.yml

services:に新しくLaravelを動かすためのサービスdbを追加します。

今回もvolumes:の設定でローカルマシンのMySQLデータと仮想コンテナのデータを同期設定しますが、 MySQLの実データはコードで管理する必要がないので、プロジェクトの中にデータを置く代わりにDockerの機能であるボリュームを使用します。 MySQLの実データを保持する場所としてfullstack-db-volumeボリュームを作成します。

1version: “3.9”
2services:
3  proxy:
4    container_name: fullstack-proxy
5    build: ./docker/nginx
6    ports:
7      - “8000:80"
8    volumes:
9      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
10      - ./src:/usr/share/nginx/html
11  app:
12    container_name: fullstack-app
13    build: ./docker/php
14    volumes:
15      - ./docker/php/php.ini:/usr/local/php/php.ini
16      - ./src:/var/www/html
17  db:
18    container_name: fullstack-db
19    build: ./docker/mysql
20    ports:
21      - 3306:3306
22    volumes:
23      - fullstack-db-volume:/var/lib/mysql
24volumes:
25  fullstack-db-volume:

Dockerのボリュームの定義であるvolumes:はdocker-compose.ymlのトップレベルに書き、services:の配下に書くのではないことに注意してください。

一方、dbサービスのvolumes:については、ローカルマシン側はfullstack-db-volume、仮想コンテナ(ゲスト)側は/var/lib/mysqlを指定します。

ポート番号ports:3306:3306とある通り、ローカルマシン(ホスト)も仮想コンテナ(ゲスト)もデフォルトポートの3306を設定しています。

docker/mysql/Dockerfile

NginxやPHPと同様に、MySQL8.0のイメージをDockerfileに指定しました。

1FROM mysql:8.0

M1チップ搭載のMacを利用している場合Dockerイメージがpullできずに失敗することがあります。
その場合は--platform linux/amd64オプションをつけると実行できるようです。
参考:https://github.com/docker-library/mysql/issues/778

1FROM --platform=linux/amd64 mysql:8.0

環境変数の設定

MySQLから参照される環境変数をdocker-compose.ymlに設定しておきます。

1...
2  db:
3    container_name: fullstack-db
4    build: ./docker/mysql
5    ports:
6      - 3306:3306
7    volumes:
8      - fullstack-db-volume:/var/lib/mysql
9    environment:
10      MYSQL_DATABASE: laravel_db
11      MYSQL_ROOT_PASSWORD: rootpassword
12      MYSQL_USER: user
13      MYSQL_PASSWORD: password
14volumes:
15  fullstack-db-volume:

dbサービスのenvironment:にMySQLの初期データベース名(MYSQL_DATABASE)とルートユーザのパスワード(MYSQL_ROOT_PASSWORD)を設定します。 任意でMYSQL_USERMYSQL_PASSWORDも設定できます。

1services:
2  ...
3  db:
4    container_name: fullstack-db
5    build: ./docker/mysql
6    ports:
7      - 3306:3306
8    volumes:
9      - ./docker/mysql/my.cnf:/etc/mysql/my.cnf
10      - fullstack-db-volume:/var/lib/mysql

コピーしたmy.cnfのファイルの一番下に追加で2行の設定をしておきます。 タイムゾーン(defualt_time_zone)を日本時間であるAsia/Tokyoに、 デフォルト文字コード(character-set-server)をutf8mb4に設定します。

1...
2[mysqld]
3pid-file        = /var/run/mysqld/mysqld.pid
4socket          = /var/run/mysqld/mysqld.sock
5datadir         = /var/lib/mysql
6secure-file-priv= NULL
7
8# Custom config should go here
9!includedir /etc/mysql/conf.d/
10
11# Settting for the application
12default_time_zone=Asia/Tokyo
13character-set-server=utf8mb4

Dockerコンテナを再起動して確認してみる

1docker-compose up -d
2...
3Building db

docker-compose upコマンドで再度コンテナを起動します。dbサービスのDockerイメージがビルドされます。

1docker-compose exec db mysql -u root -p
2Enter Password: (rootユーザのパスワードを入力)

起動したMySQLのコンテナにログインしてインストールできたか確認してみます。

docker-compose exec (サービス名) (コンテナ内で実行したいコマンド)または
docker exec -it (コンテナ名) (コンテナ内で実行したいコマンド)
コマンドによって起動しているコンテナに対してコマンドを実行することができます。

fig4

mysqlコマンドで設定したパスワードを入力して、mysqlにログインできれば成功です。

ここまでのコード(GitHub)

phpMyAdmin(任意)

phpMyAdminを利用すると、MySQLのデータをブラウザ上で参照・編集することができます。 services:の中に、phpmyadminサービスを追加します。

1...
2  phpmyadmin:
3    container_name: fullstack-phpmyadmin
4    image: phpmyadmin/phpmyadmin
5    ports:
6      - 8001:80
7    environment:
8      - PMA_ARBITRARY=1
9      - PMA_HOST=db
10      - PMA_ROOT_PASSWORD=rootpassword

environment:にはPMA_HOSTにdbサービスのサービス名(db)、 PMA_ROOT_PASSWORDにルートパスワードを設定します。 PMA_ARBITARYは任意のサーバーに接続するかどうかの設定で、ここでは接続する(1)設定にしています。

phpMyAdmin fig5

ブラウザでlocalhost:8001にアクセスすると、ログイン画面が表示されます。 先ほど設定した値でログインすることができます。

fig6

Dockerfileとdocker-compose.ymlのどちらに設定を書くべきか

docker-compose.ymlの各サービスに記述する設定はDockerfileにも記述できます。

Dockerfileに設定を記述する基準は「永続化したいかどうか」です。 永続化したい場合や再配布したい設定はDockerfileに記述し、 環境によって変更したい・されそうな設定はdocker-compose.ymlに記述します。

2. Laravel環境構築

2-1. Composer

LaravelはphpパッケージマネージャであるComposerでインストールできます。 そのため、まずはComposerを導入します。

Composer 公式ではインストール方法がくどくどと説明されていますが、結局のところphpが動く環境で以下の1行を実行すればインストールできます。(ただし後述の理由により、以下のコマンドの実行する必要はありません。)

curl -sS https://getcomposer.org/installer | php

composer.pharというファイルが生成され、php composer.pharでComposerを実行できるようになります。

ところで、DockerコンテナでLaravelを動かすためには、Composerインストールする手順が毎回含まれてきます。 このインストール手順はDockerfile内にあらかじめ書いてしまうことで、コンテナを起動するたびに実行する手間を省けます。 Dockerイメージをビルドする段階でComposerをインストールしてしまいましょう。

1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer

RUN (ビルド時に実行させたいコマンド)
docker/php配下のDockerfileにコマンドを2行追記します。前述のComposerインストールコマンドの行と、 php composer.pharではなくcomposerコマンドでComposerを使えるようにするための行を追加しています。

Dockerfileを編集した場合は、変更をイメージに反映するためにdocker-compose buildコマンドでDockerイメージを再ビルドします。

1docker-compose build app
2Building app
3[+] Building 1.9s (7/7) FINISHED                                                                                                      
4 => [internal] load build definition from Dockerfile                                                                             0.0s
5 => => transferring dockerfile: 187B                                                                                             0.0s
6 => [internal] load .dockerignore                                                                                                0.0s
7 => => transferring context: 2B                                                                                                  0.0s
8 => [internal] load metadata for docker.io/library/php:8.1-fpm-alpine                                                          1.8s
9 => [1/3] FROM docker.io/library/php:8.1-fpm-alpine@sha256:58508918c6d212da25d38b4b422af86588b0146f1509256b38a3d1203932f512    0.0s
10 => CACHED [2/3] RUN curl -sS https://getcomposer.org/installer | php                                                            0.0s
11 => CACHED [3/3] RUN mv composer.phar /usr/local/bin/composer                                                                    0.0s
12 => exporting to image                                                                                                           0.0s
13 => => exporting layers                                                                                                          0.0s
14 => => writing image sha256:125ddc758e99ce8aed432fea227069e8180ba737f2b812632edcb0ae17723eb0                                     0.0s
15 => => naming to docker.io/library/fullstack-project_app                                                                         0.0s

出力を確認するとComposerインストールのコマンドが実行されていることがわかります。

1docker-compose up -d
2docker-compose exec app composer -v

docker-compose upコマンドで再度コンテナを起動して、Composerがインストールされたか確認してみましょう。

コンテナ内でcomposer -vコマンドを実行してComposerのロゴが表示されれば成功です。

![fig7]/images/app/204/fig7.png)

ここまでのコード(GitHub)

2-2. Laravel

Installation Via Composer - Laravel

ComposerでLaravelプロジェクトを作成します。 Laravelアプリケーションの保存先をローカルディレクトリのsrc/配下にします。 src/配下にあったindex.htmlとinfo.phpは不要なので削除した後、以下のコマンドを実行します。

1docker-compose exec app composer create-project laravel/laravel .
2
3...
4In GitDownloader.php line 77:                                                            
5  git was not found in your PATH, skipping source download

gitコマンドが使えないようなので、インストールします。phpのDockerfileに追記して、再度ビルドしましょう。

1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer
6
7# install packages
8RUN apk update
9RUN apk add git

apkコマンドをupdateした後、gitをインストールします。

docker-compose build app

1docker-compose exec app composer create-project laravel/laravel .
2
3Creating a "laravel/laravel" project at "./"
4Info from https://repo.packagist.org: #StandWithUkraine
5Installing laravel/laravel (v9.1.10)
6...
7> @php artisan key:generate --ansi
8Application key set successfully.

default.confの編集

ブラウザでlocalhost:8000にアクセスしてみると、残念ながらNginxの403エラーが表示されます(src/index.htmlを削除したため)。

fig8

そこで、Nginxのdefault.confに設定の変更を行います。

1...
2    #access_log  /var/log/nginx/host.access.log  main;
3
4    location / {
5      root   /usr/share/nginx/html/public;
6      index  index.html index.htm index.php;
7
8      try_files $uri $uri/ /index.php$is_args$args;
9    }
10
11    ...
12
13    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
14    #
15    location ~ \\.php$ {
16       root           /var/www/html/public;
17       fastcgi_pass   app:9000;
18       fastcgi_index  index.php;
19       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
20       include        fastcgi_params;
21    }
22...
location / ディレクティブの編集

ルートディレクトリを/usr/share/nginx/html/publicに変更します。
また、ブラウザでルートパス(/)を指定した場合にindex.phpを参照させるように、indexにindex.phpを追加します。
さらに、URLに含まれるパスやパラメータをLaravelアプリケーションに転送できるよう、以下の行を追加します。
try_files $uri $uri/ /index.php$is_args$args;

location ~ .php$ ディレクティブの編集

Laravelアプリケーションの開始点となるindex.phpはpublicディレクトリの中にあります。 末尾にphpファイルが付く場合のルートも/var/www/html/publicに変更します。

default.confの編集が完了したらコンテナを再起動します。

Laravelのサーバー要件を満たす

Laravelを動かすためにはPHP以外にもいくつかの拡張モジュールが必要です。

Server Requirements - Laravel

Dockerfileではdocker-php-ext-installコマンドでPHPの拡張モジュールをインストールすることができます。 ここではLaravelでMySQLを扱うためにpdopdo_mysqlをインストールしています。

1FROM php:8.1-fpm-alpine
2
3# install composer
4RUN curl -sS https://getcomposer.org/installer | php
5RUN mv composer.phar /usr/local/bin/composer
6
7# install packages
8RUN apk update
9RUN apk add git
10
11# install php extensions
12RUN docker-php-ext-install pdo pdo_mysql

データベースの接続設定とマイグレーション

Laravelの環境変数.envに、Dockerのデータベースの情報を設定します。

1...
2DB_CONNECTION=mysql
3DB_HOST=db
4DB_PORT=3306
5DB_DATABASE=laravel_db
6DB_USERNAME=root
7DB_PASSWORD=rootpassword
8...

DB_HOSTはDockerのサービス名dbDB_DATABASEDB_USERNAMEDB_PASSWORDもDockerで設定した値にします。

1docker-compose exec app php artisan migrate
2...
3Migration table created successfully.
4Migrating: 2014_10_12_000000_create_users_table
5Migrated:  2014_10_12_000000_create_users_table (135.82ms)
6Migrating: 2014_10_12_100000_create_password_resets_table
7Migrated:  2014_10_12_100000_create_password_resets_table (68.97ms)
8Migrating: 2019_08_19_000000_create_failed_jobs_table
9Migrated:  2019_08_19_000000_create_failed_jobs_table (66.53ms)
10Migrating: 2019_12_14_000001_create_personal_access_tokens_table
11Migrated:  2019_12_14_000001_create_personal_access_tokens_table (116.13ms)

Dockerコンテナを再起動して確認してみる

ブラウザで<Command>localhost:8000</Command>にアクセスし、Laravelの初期画面が表示されれば成功です。

fig9

ここまででLaravelが動く環境が用意できました。

ここまでのコード(GitHub)

後編に続く

後編に続きます。

Docker+Laravel+React+TypeScript構成のフルスタックパッケージを順を追って構築する【後編:フロントエンド】