Remixのv1.12.0で導入された目玉機能「新しい開発用サーバー」について

2022年の1月31日、Remixの最新バージョン**1.12.0**が公開された。

https://github.com/remix-run/remix/releases/tag/remix%401.12.0#user-content-changes-by-package

この新しいアップデートにおいて、大きな目玉となっているのが新しい開発用サーバーである。

これは今までRemixが取っていたアプローチである、Remix App Server以外のサーバーに対する開発環境下での抽象化させすぎない戦略に対する新たな試みとして実装されている。

ただ、日本語の記事などではあまり大きく取り上げられていないため、今回その新しい開発用サーバーについて少し解説を行ってみる。

:::note warn
Remixの開発チームのコンテキストを理解していないと、リリースノートをかいつまんでは理解できない内容になっているため、解釈の相違がある場合があります。間違い等があればコメント欄で指摘していただけると修正致しますので、ご指摘等よろしくお願いいたします。
:::

# 読み進める前に知っておいてほしいremix CLIのコマンド三選

今回導入された新しいアプローチが、そもそも採用されるに至った経緯には、今までのremix CLIが抱えていた問題点によるものだった。

そのため、読み進める前に、具体的に３つのコマンドについて改めて内容を確認してほしい。

## `remix watch`コマンド

アプリケーションファイルの変更を監視する。変更があればビルドを行い、**build**ディレクトリに**index.js**を生成する。

## `remix dev`コマンド

`remix watch`コマンドの内容に加えて、Remix App Serverを起動する点が異なる。
これによりターミナルでURLが出力されるので、ブラウザからアプリにアクセスできるようになる。

```shell
...
[0] Rebuilding...
[1] 💿 Built in 208ms
[1] Remix App Server started at http://192.168.xx.x:3000
[0] Done in 111ms.
[1] GET / 200 - - 41.823 ms
```

## `remix-serve`パッケージ

`remix-serve`パッケージは、Remixアプリを実行するために必要な設定が施されたexpressアプリケーションを作成する。

そして、この内容は**remix CLI**に統合されているので、`remix watch`によって起動されたウォッチャーと`remix-serve`によって実行されるexpressサーバーを同じプロセスで実行することができるようになっている。

これは、`nodemon`や`wrangler`を使って独自のサーバーを起動させたくない開発者のために用意されたものだ。

# 今までの問題点

remix CLIでは、`remix dev`コマンドが組み込まれている。
これを使うことで、`remix watch`を実行しつつ、Remix App Serverを立ち上げることができるようになっている。

Remix App Server以外のサーバーを実行したい場合は`remix dev`コマンドを使うことができない。
ではどのように今まではしていたのかというと、`remix watch`コマンドを実行してファイルを吐き出させて、自分の使用したいサーバーに合わせて`wrangler`や`nodemon`を実行してサーバーを起動させる方法を実践していた。
`remix watch`コマンドによってファイルの変更が監視されているので、再ビルドされたときは`<LiveReload>`がWebソケットを通じて、ブラウザにリロードするように伝えるようになっている。

上記の内容を読んでも分かる通り、今までの`remix dev`コマンドは、内蔵されているRemix App Serverとの関係性から、Remix App Server以外のサーバーで開発を行う場合に制限が発生していた。
もちろん自前のRemix App ServerをRemixが所有しているのは大きなメリットなのだが、`wrangler`や`nodemon`といったツールを使用して開発用のサーバーを起動したい場合にはそうはいかない。
Remix App Server以外のサーバーを使いたい場合は、`remix watch`と同時に`wrangler dev`や`nodemon server.js`を実行する必要があった。

しかし、これの問題点として`wrangler`や`nodemon`を使って起動したサーバーがサーバー用のコードをリブートしたかどうかをRemixが知る術は存在しなかった。

そして、`remix dev`でも問題点があり、今まではサーバー自体のリブートは行わず、Node.jsの`require`のキャッシュを削除するのみにとどめていた。

これらの問題点が組み合わされることにより、開発環境では大きく分けて４つの問題が浮上していた。

## 1. 再ビルドによるPrismaの接続数枯渇問題

キャッシュがクリアされてしまうので、DB接続やメモリ内の参照はもちろん削除される。そのため、再ビルドされるたびにPrismaを使って新しい接続をおこなってしまうため、最終的に接続数が足りなくなるという問題が発生していた。

これを回避するためには、キャッシュのクリア前に接続していたという状態を維持させておくために`global`を使って`__db`フラグを設定するなど、少々トリッキーな回避策を自前で用意する必要があった。

この問題への具体的な実装方法については、こちらのチュートリアルでも言及されている。

https://remix.run/docs/en/v1/tutorials/jokes#connect-to-the-database

## 2. Webソケットのポート番号指定問題

`<LiveReload>`に使用するWebソケットのポートを`remix watch`と別のプロセスで起動しているサーバーに対して指定しようとする場合、ものすごく面倒で大変な作業が発生します。

というのも、上記の問題はRemixアプリを２つ同時に開発している場合における問題点である。

`wrangler dev --port 3000`や`nodemon server.js --port 3001`を実行して立ち上げられたサーバーは２つのプロセスで実行されたアプリであるものの、オプションを経由してポート番号を指定することができるのでそこまで面倒な操作は発生していない。

しかし、`remix watch`を実行する場合は簡単にはいかない。
`<LiveReload>`は`remix watch`の機能の一部である。
そして`<LiveReload>`はWebソケットを使用しているため、当然ポート番号を指定しないといけない。そしてサーバーバンドルであるSSRとクライアントバンドルであるブラウザの両方でそのポート番号を知っておく必要がある。

つまり、`remix watch`と`wrangler dev`の両方で正しいWebソケットのポート番号を取得しないといけないことを意味しており、今までのremix CLIでは簡単にオプション経由でポート番号をウォッチャーと呼ばれる`remix watch`に渡すことは不可能だった。
なので、その都度環境変数を設定するなどして、ウォッチャー側にポート番号を渡す必要があったのである。
これはあまりに面倒であり、開発体験を損なう結果に繋がっていると度々問題点として上がっていた。

## 3. HMRの動作の一貫性問題
上記の理由から、HMRといった新しい開発機能を、Remix App Serverを含めたすべてのサーバーで一貫して動作するように実装することが困難を極めていた。

## 4. 再ビルドによるパフォーマンス問題
再ビルドされるたびに、**build**ディレクトリと**public/build**ディレクトリには、新しいファイルが生成される。
これらの生成されたファイルは開発用として使用しているサーバーがクラッシュするか正しく終了するかしない限り、ずっと残り続けることになる。そしてそのファイル数は数千ファイルにも及ぶため、パフォーマンス問題を引き起こし、デバッグ時に混乱を招くことにつながっていた。

# 開発用サーバーという新しいアプローチ

上記における問題の解決方法として、**v1.12.0**から**新しい開発用サーバー**が実装された。

この新しい開発用サーバーは、次のことを実行する。

- 開発モードでアプリをビルドする
- アプリのファイル変更を監視し、変更のたびに再ビルドを行う
- ブラウザでの再読み込みを行うときは、必ず`nodemon`などで起動したアプリサーバーが準備完了のシグナルを通知するのを待ってから行う
- 以前のようなインメモリキャッシュの削除は行われない
- Prismaの接続を維持するための`global`を使ったハックは必要なくなる

具体的なメリットはなんなのかというと、２つのアプリ開発時に発生する両方のプロセスを所有する永続的なサーバーが存在することによって、ブラウザに何かを通知するタイミングを制御できるようになったことが一番のメリットとされている。

そして後述するが、複数のプロセスで起動したアプリの管理で大変だったWebソケットポート番号などを**remix.config.js**の`future`フラグで一元管理できるようになったこともまた大きなメリットのひとつだと言える。

https://github.com/remix-run/remix/issues/4838

## 現在開発用サーバーではできないこと

新しく実装された開発用のサーバーはRemix App Server、すなわち`remix-serve`とは現時点では連動できない。

これは、Remix App Serverが`NODE_ENV=production`としてハードコーディングされてしまっているためだとされている。

:::note 
しかし、この新しい開発用のサーバーが安定的になった段階でRemix App Serverに対応することが予定されているので安心してよい。
:::

# 新しい開発用サーバーを試す

この新しい開発用のサーバーは安定版ではないため、注意して使用する必要がある。

まずは、**remix.config.js**で、`future.unstable_dev`を`true`に設定する。

```remix.config.js
module.exports = {
  future: {
    unstable_dev: true,
  },
};
```

もし、開発用のサーバーのポート番号等を設定しつつ試したい場合は`true`の代わりにオプションを表すオブジェクトを渡す。

```remix.config.js
module.exports = {
  future: {
    unstable_dev: {
      // Port to use for the dev server (i.e. the <LiveReload> websocket)
      // This can be overridden by a CLI flag: `remix dev --port 3011`
      // By default, we will find an empty port and use that
      port: 3010,

      // Port for running your Remix app server
      // This can be overridden by a CLI flag: `remix dev --app-server-port 3021`
      // default: `3000`
      appServerPort: 3020,

      // Path to the Remix request handler in your app server
      // Most app servers will route all requests to the Remix request
      // handler and will not need this option. If your app server _does_
      // route only certain request paths to the Remix request handler, then
      // you'll need to set this.
      // default: `""`
      remixRequestHandlerPath: "/products",

      // The number of milliseconds between "readiness" pings to your app server
      // When a Remix rebuild finishes, the dev server will ping a special
      // endpoint (`__REMIX_ASSETS_MANIFEST`) to check if your app server is
      // serving up-to-date routes and assets. You can set this option to tune
      // how frequently the dev server polls your app server.
      // default: `50`
      rebuildPollIntervalMs: 25,
    },
  },
};
```

:::note warn
この設定内容は将来的に変更される可能性があるので注意が必要
:::

現段階では、実際に起動するときは２つのコマンドを実行する必要がある。

１つは、`remix dev`による新しい開発用のサーバー。
そしてもう一つは`nodemon`などによって起動されたアプリ用のサーバー。

```shell
# spin up the new dev server
remix dev

# Spin up your app server in parallel.
# This can be done in a separate terminal or with a tool like `concurrently`.
nodemon --watch build/ ./server.js
```

# Remix v1.12.0のまとめ

Remix v1.12.0では、新たに開発用サーバーという試みが導入された。

これは、Remixフレームワークが成長していく過程で発生した`<LiveReload>`などにおけるポートの問題などが開発体験を将来的に損なう可能性があったことから、この段階での機能追加に踏み切ったのだと考えられる。

もちろん安定版の機能ではないため、将来的に廃止されるか、もしくは設定方法等が大きく変更される可能性は存在する。

しかし、このような実験的な試みはRemixの開発チームが開発者のための開発体験を向上させながら進化をしていこうという前向きな思いの表れだと考えている。

もし、まだRemixを使っていない読者の方がいたら、ぜひRemixフレームワークを導入していただき、その開発体験の良さを一度味わってほしいと切に願う。

:::note warn
誤訳や解釈の相違がある場合があります。間違い等があればコメント欄で指摘していただけると修正致しますので、ご指摘等よろしくお願いいたします。
:::