[Next.js] 開発中のページを開発環境でのみ表示し、本番環境のビルドには含めないようにする
はじめに
この記事について
こんにちは、 @zomysan(Twitter) です。この記事では、Next.js で開発をしているWebアプリケーションのフロントエンドを対象に、開発途中のページをどう扱うかということについて書きます。
新しい機能やリニューアルのための開発を始めてあたらしいページを追加したものの、まだ途中なのでユーザーに見せられる状態ではない、ということはよくあると思います。ユーザーには見せたくないけど、開発環境やステージング環境では確認したい。でも本番環境には出したくない。そういうときどうしたら良いのでしょうか?
この記事の対象
この記事は以下のような人を対象としています。
- Next.js で Web アプリケーションを実装している
- 開発中のページを本番環境に露出したくない
まとめ
今回、私は以下のように実現してみました。
- 開発中のページについて、拡張子を
.page.dev.tsx
にする- 公開済みのページは
.page.tsx
にする
- 公開済みのページは
- next.config.js で
pageExtensions
の設定を環境変数によって切り替え、本番環境では.dev.page.tsx
をビルドに含めないようにする
ここに至るまでの過程や詳しい内容について書いていきます。
解決策の案
リリースできるようになるまでブランチを分けて開発する
もっともわかりやすく、やりやすい解決策です。メインのブランチ( main
)から新しい画面を追加するブランチ( new-feature
)を切り、リリースできる状態になったら main
にマージしてリリースを行います。
直感的でわかりやすいいっぽう、開発が長引いて main
と new-feature
の間に大きな乖離が出てくるとコンフリクトなどが辛くなります。こまめに main
を new-feature
にマージすれば大丈夫そうですが、他のブランチ other-new-feature
が同じく存在しうることを考えると、あまりブランチを長期間生存させるのは望ましくありません。(そもそも、そこまで開発が長期化するというのがマズイというのはありますが)
リンクを繋がないようにする
こちらもシンプルで、ページとしては存在するものの、リンクを貼らなければユーザーの導線としては存在しない、という考え方です。
何がなんでも秘匿したいわけではないのであれば、十分に選択肢として有効です。図にもあるとおり、ユーザーがURLを直打ちすればページにアクセスできてしまいます。URLがわからない場合も、Next.jsの設定次第ではブラウザなどでJavaScriptのソースコードを読めば内容がわかってしまうこともありますので、新機能を隠しておきたいときなどには不向きです。
開発環境でのみページが存在するようにする(今回はこれ)
今回私が採用した方法です。Next.jsの設定を変更することで、本番環境においては開発中のページをビルド自体から外し、完全に存在しないものとして扱います。開発環境では普通にページとしてアクセスできます。
リリースしてもこのページはビルドに含まれない(JavaScriptのソースコードにも含まれない)ので、開発途中でリリースに含まれても大丈夫です。
おまけ:リリースするときだけ対象ページを手動で削除して、リリースが終わったら戻す
あまりにもシンプルな解決策ですが、暫定的な対応ならこれでもアリかもしれません。ただし、リリースのたびにそのような手順が必要になると面倒ですし、忘れるというリスクもあります。そもそもリリースの前にソースコードを触るというのが怖いですよね。あまりおすすめできません。
pageExtensions
について
Next.js の では、ここからは具体的に、どうやって開発中のページを本番環境でのみビルド対象から外すのか、ということについて見ていきます。まずは Next.js の設定のひとつ、 pageExtensions
を触ってみましょう。
pageExtensions
とは
Next.js では、 /pages
以下に置かれたファイルひとつひとつをページとみなし、ファイル構造と同じルーティングを生成してくれます。これについて、「何をページとしてみなすのか?」を指定するのが pageExtensions
です。
pageExtensions
のカスタム例
デフォルトでは .ts
, .tsx
, .js
, .jsx
がページとしてみなされます。が、 Colocation (関連するコードを近くに置く)の考え方に基づいてファイルを構成していると、 pages
にページ本体以外のファイルも置きたくなってくると思います。例えば、以下のようなものです。
- ページから使うコンポーネント(
.tsx
など) - ページから参照している画像(
.png
,.svg
など) - ページの単体テスト(
.spec.ts
,.test.ts
など) - ページのスタイルシートのファイル(
.css
,.scss
など) - etc...
これらのページじゃないけどページに必要なファイルたちと、実際にルーティングに含めてほしいページ本体を区別するために pageExtension
の設定を変更できます。ページ本体の拡張子を .page.tsx
にし、pageExtension
を以下のように変更します。
module.exports = {
pageExtensions: ['page.tsx'],
}
こうすることで、コンポーネントや単体テストがルートに含まれなくなります!このような設定を採用しているプロジェクトは多いのではないでしょうか。
pageExtensions
を切り替える
開発環境と本番環境で それでは、この pageExtensions
を使って実際に本番環境で開発中のページが表示されないようにしてみましょう。
具体的なファイル構成例
以下のようなファイル構成を例にとって説明します。このうち、 src/pages/mypage/config/index.page.tsx
が今回開発していきたいページです。
.
├── src/
│ └── pages/
│ ├── index.page.tsx
│ ├── Welcome.tsx
│ └── mypage/
│ ├── index.page.tsx
│ └── config/
│ └── index.page.tsx # 今回開発を進めたいページ
├── next.config.js
├── package.json
├── tsconfig.json
└── ...
module.exports = {
pageExtensions: ['page.tsx'],
}
相対URLでいうと、以下のようなページがあることになります。
-
/
- トップページ
- ファイル:
src/pages/index.page.tsx
-
/mypage
- マイページ
- ファイル:
src/pages/mypage/index.page.tsx
-
/mypage/config
- 設定ページ(今回開発を進めたいページ)
- ファイル:
src/pages/mypage/config/index.page.tsx
Welcome.tsx はトップページから利用しているコンポーネントであり、拡張子が .page.tsx
ではないため、ページとしては存在しません。
この状態で開発サーバーを立ち上げると、 /mypage/config
にアクセス可能です。拡張子が .page.tsx
であり、ページとしてルーティングに含まれているので、当然の結果です。
まずは拡張子を変更してみる
では、一旦 /mypage/config
をルーティングとビルドから外してみましょう。拡張子を .page.tsx
から変更すればよいので、簡単です。
.
├── src/
│ └── pages/
│ ├── index.page.tsx
│ ├── Welcome.tsx
│ └── mypage/
│ ├── index.page.tsx
│ └── config/
│ └── index.page.dev.tsx # .page.tsx -> .page.dev.tsx に変更
├── next.config.js
├── package.json
├── tsconfig.json
└── ...
このように変更し、/mypage/config
にアクセスしてみましょう。404ページが表示されましたね!
pageExtensions
を変更する
いっさい表示されないままでは困ります。開発環境ではちゃんと表示されて、本番環境では表示されないようにしたいですよね。pageExtensions
の設定を変更していきましょう。
環境変数の定義(開発環境)
開発環境と本番環境で設定を切り替えるため、環境変数を使います。ここでは、 true | false
いずれかの値が入る IS_DEVELOPMENT
という環境変数を定義します。
Next.js はローカルに置いたファイルから環境変数をロードしてくれるので、開発環境ではこれを利用しましょう。
.env.local
ファイルを作成します。内容は以下です。
IS_DEVELOPMENT=true
pageExtension
の設定
そして、IS_DEVELOPMENT
によって pageExtensions
が設定されるよう、以下のように next.config.js を修正します。
const nextConfig = {
pageExtensions: # 変更するプロパティ
process.env.IS_DEVELOPMENT === 'true'
? ['page.tsx', 'page.dev.tsx']
: ['page.tsx'],
};
環境変数の定義(本番環境)
本番環境においても IS_DEVELOPMENT=false
を設定します。CI でビルドしているならそのコンテキストでの環境変数、Vercel や Render などのサービスを利用しているならそのサービスの環境変数で設定します。(厳密にはこれを設定しなくても意図通りに動きますが、私は一貫性のために設定しています)
おわりに
もちろんこれ以外にも方法はありそうですが、いまのところもっともシンプルな解決策だと判断し一旦このやり方を採用しました。もっといい方法をご存知の方がいましたら、ぜひコメントで教えてほしいです!
Discussion