複数案件を掛け持ちする場合にホスト名とポート番号を使ってローカル環境をうまく切り分けたい

## はじめに
複数案件を掛け持ちする場合にホスト名とポート番号を使ってローカル環境をうまく切り分けたいなと思いました。

まず複数の案件の掛け持ちについてですが、ざっくりいうと複数のリポジトリを切り替えながら開発をしたり、あるプロジェクトの修正をして、PRを出したらレビュー待ちの間に並行して別のプロジェクトの修正をして…、というようなケースです。

最近はReact（Vite）やSupabaseの開発を行っていますが、どちらもローカル環境の構築がスムーズにできてとても便利です。

その一方でブラウザでローカル環境にアクセスする場合に、立ち上げの際にポート番号が衝突したり、都度都度ポート番号を確認したり、意図しないプロジェクトに繋いでしまったりと苦労していました。

他には最近はCursorなどのAI Agentをつかって開発をしていますが、Yoloモードの際に `npm run dev` などをし直された際にポート番号が無尽蔵にズレていく、みたいなことも起きていました。

色々と工夫してある程度目処がたったのでここにまとめます。

## ポート番号の固定と取り決め

まずはプロジェクト（リポジトリ）ごとにポート番号を固定するようにしました。

よくあるReactなどのフロントでは、 `npm run dev` などでローカル環境を立ち上げた際にはフレームワークで使われるデフォルトのポート番号を起点として、もしそのポート番号が使われていれば次のポート番号にずらして立ち上げが行われます。
そうしてしまうと、3000、3001、3002…　といった具合にどのポートがどのプロジェクトに紐づいているか、ということが流動的になってしまい、ブラウザのURLで直打ちする場合に確認しながらずらしていく、という作業が必要だったり、 npm run dev で起動したプロンプトを探してそこから開く、という作業が必要でした。
他にもCypressなどのE2Eテストを導入しようとした際、ポート番号が固定されていないと都度変更するか、ポートを開けたりする必要があり、何かとストレスが溜まっていました。

そこでまずは、フロントの設定でポート番号を固定するようにしました。
まずはそれぞれのプロジェクトをチーム全体で管理することとし、その上でプロジェクトに通し番号を振り、その番号を元にポート番号をずらす（オフセットする）ようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/659783e3-bf02-4be3-bdc7-8f0ee843dfce.png)

## Viteでの例

下記はViteの例で、デフォルトのポート番号から13を増やすようにしています。
明示的にずらすように、 `5173 + 13` としています。


```ts:vite.config.ts
export default defineConfig({
  ...
  server: {
    port: 5173 + 13,   // 5186: 3番目のプロジェクトなので、10のオフセットと3を足したポート番号。
    strictPort: true,  // 厳格モード。ポートが取れない場合にエラーにする。  },
  ...
});
```

## Nextでの例
実際に試してないですが、多分下記のような内容になるようです。

```json:package.json
{

  scripts: {
    "dev": "next dev --port 3013",
  }
  
}
```

## Supabase
Supabaseのローカルでは利用しているポート番号が多いので、ポート番号は10ずつずらすようにしました。それらのポート番号の指定は config.toml の中で一斉に書き換えました。
都度書き換えるのが面倒なのでCursorに指示を出して書き換えしてもらいました。

```
Supabaseのポート番号について、すべてのローカル開発でのポートを 30 ずつ増やしたものにしてください。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/b360e196-85bc-44ab-afd1-be1731c516e0.png)

書き換わったのは下記のようです。

- APIポート: 54321 → 54351
- DBポート: 54322 → 54352
- DB Shadowポート: 54320 → 54350
- DB Poolerポート: 54329 → 54359
- Studioポート: 54323 → 54353
- Inbucketポート: 54324 → 54354
- Analyticsポート: 54327 → 54357

```toml:config.toml

[api]
port = 54351

[db]
port = 54352
shadow_port = 54350

[db.pooler]
port = 54359

[studio]
port = 54353

[inbucket]
port = 54354

[analytics]
port = 54357
```

supabase status すると下記のように変わっていることが確認できます。

```bash
supabase status

         API URL: http://127.0.0.1:54351
     GraphQL URL: http://127.0.0.1:54351/graphql/v1
  S3 Storage URL: http://127.0.0.1:54351/storage/v1/s3
          DB URL: postgresql://postgres:postgres@127.0.0.1:54352/postgres
      Studio URL: http://127.0.0.1:54353
    Inbucket URL: http://127.0.0.1:54354
...
```

## ホスト名
これである程度、環境によってポート番号の衝突を防ぐことができるようになりました。
ですが今度は、「あれ、このプロジェクトってどのポート番号だったっけ…」と考えてしまうようになりました。
特に大体プロジェクトを開くときはブラウザに直打ちしているので、localhost.... と打ち込む途中で補完されはするのですが、どのポート番号がどのプロジェクトか、ということの判断が難しかったです。
フロントの方はルーターの違いによりある程度予測しながら作業できるのですが、SupabaseのStudioの方はURLが基本的にどのプロジェクトも同じ構成になっているので見分けがつきません。

かといってこのプロジェクトが何番だから、ポート番号はデフォルトの54321に足して…　とやるのも、10を超えてきた時点で 54401 などとなり混乱してきてしまいました。

そこでホスト名を絡めて管理する、という方法をとりました。

## ホスト名をローカルに向ける

まず前提として、自分たちのチームでは「リポジトリ名」をいろいろなものの基準にするようにしました。（チームでは、というより自分自身が長く開発に携わらせてもらっているなかで、プロジェクト管理の都合上、リポジトリ名を慎重に選ぶようにしてきています。）

このリポジトリ名を基準にしてホスト名を決め、そのホスト名をローカルに向ける、すなわちプロジェクトのホスト名の解決を 127.0.0.1 にするようにしました。

例えば `markify` というリポジトリに対して、　markify.test でローカル向きのアクセスができるようにする、ということです。
こうすることで、 `markify.te` のようにURLに打ち込めば、ポート番号が何だったかを覚えていなくても補完が効きます。もちろん補完ですので初回には手動でアクセスが必要だったり、間違えて記録させてしまった場合には[消去](https://support.google.com/chrome/answer/95589?hl=ja&co=GENIE.Platform%3DDesktop)（URLの補完リストからバツボタンを押せばOKです） が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/e8395d3a-3a42-4e0c-8e6a-3282051a7ca3.png)

こうすると便利です。

## DNSで *.test をすべてローカルに向ける（macOS）

まずは `*.test` なドメインをすべてローカルに向けるようにします。

それほど複雑ではありませんが流れとしては
1. dnsmasqの導入
1. resolveの仕組み（？）で向き先をdnsmasqにする
1. 起動

です。

```
brew install dnsmasq
```

```conf:/opt/homebrew/etc/dnsmasq.conf
address=/test/127.0.0.1 # これで *.test が 127.0.0.1 に向きます。
```

resolverファイルをディレクトリと一緒に作成します。

```bash
sudo mkdir -p /etc/resolver
sudo tee /etc/resolver/test <<EOF
nameserver 127.0.0.1
port 53
EOF
```

```
sudo brew services start dnsmasq
```

ここまでできれば ping でローカルホスト（127.0.0.1）に向いていることを確認します。
`nslookup` や `host` では解決できないので `ping` を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/3d5d3324-3ec7-44b2-9a1d-bc0783b158c0.png)


## ホスト名の設定（Supabaseにはない）
Viteについてはホスト名の指定ができるのでしておきます。

```ts:vite.config.ts
export default defineConfig({
...
  server: {
    host: 'markify.test',  // ここでホスト名追加
    port: 5173 + 19,
    strictPort: true,
  },
...
});  
```

この状態で起動するとホスト名が指定されて起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/7f4ea3b1-edf0-4eaf-b12f-80d0492972a8.png)

なお見出しにもあるようにSupabaseにはありません。

## まとめ
- 複数プロジェクトの管理にはポート番号を固定しながらずらす
- ホスト名も一緒に変えると混乱を防げる
-