パス単位でVueからReactにリプレイスする

# 1. 背景

https://tech.iimon.co.jp/entry/2024/02/13/165759

業務で現在NuxtからReact(+vite)にリプレイスしています。
現在リプレイスの進め方はパス単位で検討しています。

# 2. パス単位で検討している理由
- 大規模なリプレイスかつ、マニュアルテストが殆どのためバグのリスクが大きいです
- 現在リプレイス作業者は私のみ（インフラはインフラチームと連携）の為、まめに進め、運用チームに機能追加、修正を引き継ぎたい為です
- ページ間でstateの引き継ぎの処理が少なかったです

# 3. 同一ドメインでパスの切り替えをする

## 3.1. 前提
- レガシーサイト（Nuxt）とリニューアルサイト（React）のパス切り替えでlocalStorageを保持したい

## 3.2. localで検証

以下のようなイメージです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/d4b2f574-ee70-7450-7870-1258578a7c9e.png)


リバースプロキシーを使います。
これによりレガシーサイト（Nuxt）とリニューアルサイト（React）のドメインを同一にでき、localstorageを保持できます。

リバースプロキシーには[nginx](https://www.nginx.com/)を使用しました。

```docker-compose.yml
version: '3'

services:
  reverse-proxy:
    image: nginx
    volumes:
      - ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 80:80
```

```reverse-proxy/nginx.conf
events{

}

http {
    server {
        listen 80;
        server_name localhost;

        #############↓リニューアルサイト##################
        location /user/history {
            proxy_pass http://host.docker.internal:3001/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }

        location /@vite/ {
            proxy_pass http://host.docker.internal:3001/@vite/;
        }

        location /src/ {
            proxy_pass http://host.docker.internal:3001/src/;
        }

        location /node_modules/ {
            proxy_pass http://host.docker.internal:3001/node_modules/;
        }

        location /@react-refresh {
            proxy_pass http://host.docker.internal:3001/@react-refresh;
        }

        location /public/ {
            proxy_pass http://host.docker.internal:3001/public/;
        }
        #############↑リニューアルサイト##################

        #############↓レガシーサイト##################
        location / {
            proxy_pass http://host.docker.internal:3000/;
            proxy_http_version 1.1;
            # 転送先にアクセス元のIPやホスト情報を再定義・追加
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
        ###############################################
    }
}
```

### viteの設定

[【Vue3】Viteで起動したローカル開発サーバーにIPアドレスで外部からアクセスする方法](https://qiita.com/Junpei_Takagi/items/3615505dcabd2e97f3e1)

strictPort、hmrの設定は以下を参考にしました
[Vite サーバーオプション](https://ja.vitejs.dev/config/server-options.html)

```vite.config.ts

server: {
    port: 3001,
    host: true,
    strictPort: true,
    hmr: {
      protocol: "ws",
      clientPort: 3001,
    },
  },
```

### nuxtの設定

[Nuxt.js の起動時に外部アクセス可能なIPを指定する](https://blog.mintsu-dev.com/posts/2020-08-04-virtualbox-nuxt/)

```nuxt.config.js
server: {
    host: '0.0.0.0',
  },
```
## 3.3. CloudFrontで検証

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/cc357e65-4df9-5f5a-0e1a-7781422a5e0d.png)

### Behaviors
Behaviorsを以下のようにしました
| Precedence | Path pattern | Origin or origin group |
| ---- | ---- | ---- |
| 0 | /assets* | renewal |
| 1 | /user/history* | renewal |
| 2 | Default (*) | legacy |

また、BehaviorのCache policyをCachingDisabledにしました

### CloudFunctions

https://zenn.dev/redgosho/articles/13b93d2a8eac0f

user/historyでアクセスしてもSPAではhistoryというファイルはない為、404 NotFoundになってしまいます

そこでCloudFunctionsでindex.htmlにリダイレクトさせます。

```js
var indexPage = "index.html"

function handler(event) {
    console.log(event);
    var request = event.request;
    var currentUri = request.uri;
    var doReplace = request.method === "GET" && currentUri.indexOf('.') === -1;
    if(doReplace) {
        var indexPath = `/${indexPage}`;
        request.uri = indexPath;
    }
    return request;
}
```

# 4. その他
## 4.1. リバースプロキシーとは

以下の記事を引用します
[リバースプロキシとは？|プロキシサーバーの説明](https://www.cloudflare.com/ja-jp/learning/cdn/glossary/reverse-proxy/)

1つ以上のWebサーバーの前に置かれ、クライアントからのリクエストを傍受するサーバーです。
リバースプロキシは配信元サーバーの前にあり、クライアントがその配信元サーバーと直接通信しないようにします。

![image.png](https://cf-assets.www.cloudflare.com/slt3lc6tev37/3msJRtqxDysQslvrKvEf8x/f7f54c9a2cad3e4586f58e8e0e305389/reverse_proxy_flow.png)

上の図は関係するリバースプロキシーのフローです。

D：任意の数のユーザーのホームコンピューター
E：これはリバースプロキシサーバーです
F：1つ以上の配信元サーバー

- リバースプロキシを使用しない場合
    DからのすべてのリクエストはFに直接送られ、FはDに直接応答を送信します
- リバースプロキシを使用する場合
    DからのすべてのリクエストはEに直接送られ、EはFにリクエストを送受信します
    その後、Dに適切な応答を渡します

# 5. 最後に
- リプレイス中はページ遷移で毎回reloadが走るのでそれがどれくらいユーザに負担かを考える必要があると思いました
- インフラ、サーバーの知識がないので業務を通して少しづつ深めたいです
- 弊社のインフラエンジニアの方に色々と助けて頂いたのでここに感謝の意を示します

# 6. 参考
- [【Vue3】Viteで起動したローカル開発サーバーにIPアドレスで外部からアクセスする方法](https://qiita.com/Junpei_Takagi/items/3615505dcabd2e97f3e1)
- [ロミオとジュリエットで学ぶ「ポートが開いてる」と「Listenしている」の違い](https://qiita.com/DQNEO/items/7027fda87a1ecf44b9f2)
- [nginx起動、再起動](https://qiita.com/Kaisyou/items/dadf6fe9ee93fb69e76c)
- [WebSocketについて調べてみた。](https://qiita.com/south37/items/6f92d4268fe676347160)
- [Nuxt.js の起動時に外部アクセス可能なIPを指定する](https://blog.mintsu-dev.com/posts/2020-08-04-virtualbox-nuxt/)
- [nuxt2 doc server](https://v2.nuxt.com/ja/docs/configuration-glossary/configuration-server/#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BE%8B)
- [Nuxt2からReactに移行を決めた話](https://tech.iimon.co.jp/entry/2024/02/13/165759)
- https://www.nginx.com/
- [リバースプロキシとは？|プロキシサーバーの説明](https://www.cloudflare.com/ja-jp/learning/cdn/glossary/reverse-proxy/)
- [【超図解】IPアドレスとは？プライベートIPとグローバルIPの違いをわかりやすく説明](https://zenn.dev/itpassport/articles/acefa57f6d2b76)
- [Vite サーバーオプション](https://ja.vitejs.dev/config/server-options.html)
- [VueをReactにリプレイスしてEasyからSimpleにした話](https://zenn.dev/mybest_dev/articles/90c62b850268e4)
- [AWSのCloudFront+S3でSPAするときにErrorPagesを使いたくない](https://area-b.com/blog/2017/12/02/2205/)
- [CloudFront+S3構成のSPA Webアプリでルートページ以外に直で行ったときにAccessDeniedになった件について](https://zenn.dev/redgosho/articles/13b93d2a8eac0f)
- [nginx.conf の location の優先順位を正しく理解する](https://muziyoshiz.hatenablog.com/entry/2019/06/30/203903)

本記事を読んで頂き、ありがとうございました。
いいねいただけると記事執筆の励みになりますので、参考になったと思われた方は是非よろしくお願い致します🙏