ReactとViteでMPAを構築してAmazon S3で公開する方法

# はじめに

こんにちは、もちもちMAXです。  
今回はReactでMPAを作成し、Amazon S3で公開する手順について解説していきたいと思います。

- reactでビルドしたけど、エントリーポイント1個しかないぞ？
- MPAをAmazon S3で公開ってどうやるの？
という人は見て頂ければと思います。

## React環境構築

ReactでMPAを作成するので、まずはReact環境を構築していきます。  
有名どころでは`create-react-app`コマンドがありますがこのコマンドで環境構築した場合、デフォルトでwebpackというビルドツールが使用されます。しかし、今回はviteというビルドツールを使いますので`npm create vite`コマンドで環境を作成していきます。
まず、適当なディレクトリで下記コマンドを実行します。

```npm
npm create vite
```

いくつか入力が求められるので入力していきます。私は以下の通りとしました。

```text
Project name : test
Selcet a framework : React
Select a variant : JavaScript
```

次に、作成されたディレクトリに移動してコマンドを実行します。

```text
cd test
npm install
npm run dev
```

表示されたurlにアクセスし、viteの画面が表示されれば完了です。
![vite_tutorial.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/7c5e86bc-8899-f45b-5165-a811d069c30d.png)

## 作業ディレクトリの作成

先ほど作成された`test`ディレクトリの構成をMPA用に変えていきます。MPAとしてブログを想定しているため、ホーム、記事一覧、記事の3階層を用意しています。  
具体的には作業ディレクトリは下記のようにしました。  
各階層にエントリーポイントとなるhtmlファイルとhtmlファイル内のscriptタグで読み込むためのjsxファイルを配置しています。

```text
test
 ├─node_modulesとかREADME.mdとか色々
 └─home
   ├─index.html
   ├─index.jsx
   ├─components
   ├─public  // 画像リソースなどはここに配置する
   └─article_list
      ├─index.html
      ├─index.jsx
      └─article
         ├─index.html
         └─index.jsx
```

## コンテンツ作成

各階層の`index.html`と`index.jsx`を作成します。
今回はサンプルとして各階層へ遷移する機能だけを書いています。

- test/home/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ブログホーム</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/index.jsx"></script>
  </body>
</html>
```

- test/home/article_list/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>記事一覧</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/article_list/index.jsx"></script>
  </body>
</html>
```

- test/home/article_list/article/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>記事</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/article_list/index.jsx"></script>
  </body>
</html>
```

- test/home/index.jsx

```jsx
import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM.createRoot(document.getElementById('root')).render(
  <div>
      <p>ブログホーム</p>
      <a href="/article_list/index.html">記事一覧へ</a>
      <br />
      <a href="/article_list/article/index.html">記事へ</a>
  </div>
);
```

- test/home/article_list/index.jsx

```jsx
import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM.createRoot(document.getElementById('root')).render(
  <div>
      <p>記事一覧</p>
      <a href="/index.html">ブログホームへ</a>
      <br />
      <a href="/article_list/article/index.html">記事へ</a>
  </div>
);
```

- test/home/article_list/article/index.jsx

```html
import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM.createRoot(document.getElementById('root')).render(
  <div>
      <p>記事</p>
      <a href="/index.html">ホームへ</a>
      <br />
      <a href="/article_list/index.html">記事一覧へ</a>
  </div>
);
```

起動したローカルホストで各ページへ遷移できることが確認できればOKです。
また、urlがディレクトリ構成に合わせて変化していると思います。

- ブログホーム
![blog_home.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/8e516c1b-f825-4eea-4042-e764fed830bc.png)
- 記事一覧
![blog_article_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/903f6d2c-6b5c-6d45-4d83-288f21deae68.png)
- 記事
![blog_article.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/1719d699-1852-efc6-4a5e-b1ea311504c7.png)


## ビルド設定&ビルド

これまでの手順でローカル環境でMPAを構築することができました。
後はビルドしてAmazon S3に配置するだけです。
MPAをビルドするために、`test`ディレクトリ内にある`vite.config.js`を編集します。

- vite.config.js

```javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path';

const outDir = resolve(__dirname, 'dist');

export default defineConfig({
  plugins: [react()],
  server: {
    watch: {
      usePolling: true,
    },
  },
  root: 'home',
  build: {
    outDir,
    rollupOptions: {
      input: {
        home: resolve(__dirname, 'home/index.html'),
        article_list: resolve(__dirname, 'home/article_list/index.html'),
        article: resolve(__dirname, 'home/article_list/article/index.html'),
      },
    },
  },
});
```

設定ファイルについて説明します。
`server`設定はホットリロードを有効にするための設定です。
`root`はルートディレクトリの設定で今回は`home`としています。
`build`以下がビルド設定の箇所であり、`input`以下で各階層のindexファイルを指定しています。これによって複数のエントリーポイントが作成されます。また、`home`や`article_list`といったキーはビルドで生成されるチャンクの名前となります。

ビルド設定が終われば`test`ディレクトリにて`npm run build`を実行します。
実行後、`outDir`で指定したフォルダにビルド結果が格納されていればOKです。

## Amazon S3で公開

最後にS3での公開方法です。  
まずはAWSのコンソールを開きS3でバケットを作成していきます。  

- `AWSリージョン`はユーザに近いところを選びます。  
  `バケット名`はreact-mpa-bucketとしています。  
![s3_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/d660dab5-8b2b-60c1-fa4c-4f8b177623f6.png)

- `ACL`は無効とします。  
  ![s3_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/87251483-6a17-95be-c8bc-8f5a28487659.png)


- Web上で公開するため`パブリックアクセスを全てブロック`のチェックは外します。  
  また、警告が表示されますがチェックマークを入れておきます。  
  ![s3_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/a64fea24-1ccb-9dc0-ff4d-bc59ff5391fc.png)

- `バケットのバージョニング`は無効にします。  
  ![s3_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/ccb9899e-0143-8ac6-2fd3-d17e00a22529.png)

- `暗号化タイプ`はSSE-S3とします。
  `オブジェクトロック`は無効にします。
  ![s3_5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/3e2ba67f-f6f8-0e0b-50bc-f67fd7dd003d.png)

- 作成されたバケットを開くとオブジェクトのアップロード画面が表示されるので、ビルド結果である`dist`ディレクトリの中身を全てアップロードします。  
  ![s3_bucket.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/c8f79e5f-a143-ce38-d907-fd45d7c14471.png)
  アップロードが成功すると下記の画面が表示されます。  
  ![s3_upload.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/226bef51-d047-6fc3-c2fc-6d314bca3801.png)

- 次に、Web上で公開するために`プロパティ`の最下部にある`静的ウェブサイトホスティング`を編集していきます。  
  ![s3_hosting.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/04fe5684-1dd2-e4f9-fa94-19e6b1c82e8a.png)
  静的ウェブホスティングを有効にし、インデックスドキュメントに`index.html`を設定します。
  ![s3_hosting_option.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/502f2fab-cb65-0ea3-d254-6cce4d8caaf2.png)
  これによってバケットにアクセスするためのURL`http://reract-mpa-bucket.s3-website-ap-northeast-1.amazonaws.com`が取得できます。

- 最後にバケットポリシーを編集していきます。  
  `アクセス許可`のバケットポリシーの編集を選び下記のポリシーをペーストします。  
  
  ```text
  {
      "Version": "2012-10-17",
      "Statement": [
          {
              "Sid": "PublicReadGetObject",
              "Effect": "Allow",
              "Principal": "*",
              "Action": "s3:GetObject",
              "Resource": "arn:aws:s3:::reract-mpa-bucket/*"
          }
      ]
  }
  ```

以上で、Amazon S3での公開設定は終了です。  
最後に`http://reract-mpa-bucket.s3-website-ap-northeast-1.amazonaws.com`にアクセスし、ローカルホストと同様にページが表示されたら完了です。  
![blog_home_s3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/6a47c3b3-8b9e-fb85-17a7-53e04dc6175a.png)
リンクをクリックすると、URLが変化していることが分かります。  
![blog_article_list_s3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463753/0405a5a3-91d6-e19a-5085-bee8d8d51a18.png)

## 最後に

本記事では、Reactを用いてMPAを構築しAmazon S3で公開する方法を説明しました。  
ただし、現在の公開方法では、https通信や独自ドメインの利用ができないという制限があります。  
次回の記事では、https化と独自ドメインの利用方法を解説する予定です。  

最近、ブログと自作アプリのLPを立ち上げたので是非見てってください！

ブログ↓
https://mochinochikimchi.com

アプリLP↓
https://mochinochikimchi.com/applications/mealmotion/index.html