Next.jsアプリケーションにメンテナンスモードを実装する

# はじめに

株式会社エイチームコマーステックの @hytkgami です。
ソフトウェアエンジニアとして、モバイル、Web フロントエンド、バックエンドなど広く開発に携わっています。
この記事は[Ateam Commerce Tech Inc. Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ateam-commerce-tech)の 22 日目の記事です。

データベースメンテナンスやAPIのメンテナンスに伴い、アプリケーションへのアクセスを一時的に遮断したいケースがあると思います。
今回は、VercelでホスティングしているNext.jsアプリケーションにメンテナンスモードを実装する方法を書きます。

# 要約

1. メンテナンスページを用意する
1. メンテナンスページにリダイレクトする処理を作る
1. フラグを用意し、フラグがONのときにリダイレクトさせる

# 環境

- node `v14.18.1`
- npm `6.14.15`
- next `12.0.7`

# 実装

## メンテナンスページを用意する

まずは、メンテナンスモード中にユーザに表示する画面を用意します。
`/public/maintenance.html`としてプロジェクトに配置します。

```html:maintenance.html
<html lang="ja">
  <head>
    <title>My Awesome Service | メンテナンス中</title>
    <style>
      body {
        box-sizing: border-box;
      }
      .root {
        padding: 1rem;
      }
    </style>
    <meta name="robots" content="noindex,nofollow" />
    <meta name="googlebot" content="noindex,nofollow" />
  </head>
  <body>
    <div class="root">
      <h1>メンテナンス中です</h1>
      <p>
        ご迷惑をおかけしますが、現在メンテナンス中のためサービスをご利用いただけません。<br />
        サービス再開までしばらくお待ちください。
      </p>
    </div>
  </body>
</html>
```

![スクリーンショット 2021-12-20 19.38.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68092/4113f15a-e917-4522-7f57-4802e3541f96.png)


必要に応じて画像を配置するとユーザのストレスを緩和できるかもしれません。
サービスのブランドに合わせて対応しましょう。

## メンテナンスページにリダイレクトする処理を作る

サイトへのアクセスを、すべてメンテナンスページへリダイレクトさせます。
[Next.jsのドキュメント](https://nextjs.org/docs/api-reference/next.config.js/redirects)に従い、設定ファイルを編集します。

```diff_javascript:next.config.js
 /** @type {import('next').NextConfig} */
 module.exports = {
   reactStrictMode: true,
-}
+  async redirects() {
+    return [
+      {
+        source: "/((?!maintenance.html$).*$)",
+        destination: "/maintenance.html",
+        permanent: false,
+      },
+    ];
+  },
+};
```

`/maintenance.html`以外へのアクセスが、すべて`/maintenance.html`にリダイレクトされるようになりました。

## フラグを用意し、フラグがONのときにリダイレクトさせる

ここまでの作業で、サービスへのすべてのアクセスがメンテナンスページにリダイレクトされるようになりました。
しかし、このままではメンテナンス中ではない時もメンテナンスページにしかアクセスできません。そこでフラグを実装します。

まず、環境変数を用意します。
プロジェクトルートで`.env.development`というファイルを作成します。

```.env.development
MAINTENANCE_MODE=false
```

さらに、先ほど編集した`next.config.js`で環境変数を読み込むようにします。

```diff_javascript:next.config.js
 module.exports = {
   reactStrictMode: true,
   async redirects() {
-    return [
-      {
-        source: "/((?!maintenance.html$).*$)",
-        destination: "/maintenance.html",
-        permanent: false,
-      },
-    ];
+    return process.env.MAINTENANCE_MODE === "true"
+      ? [
+          {
+            source: "/((?!maintenance.html$).*$)",
+            destination: "/maintenance.html",
+            permanent: false,
+          },
+        ]
+      : [];
   },
 };
```

環境変数`MAINTENANCE_MODE`が`true`の場合にメンテナンスページへのリダイレクト処理を行い、そうでない場合は何も設定を渡しません。
こうすることで、メンテナンスモードがONのときにリダイレクトをかける、といった要件を実現できます。

:::note warn
環境変数 MAINTENANCE_MODE に渡している true や false は文字列として解釈されます。
:::


### フラグがOFFのときにメンテナンスページにアクセスさせない

ついでに、フラグがOFFのときの挙動も定義します。
先ほどの例では、

> そうでない場合は何も設定を渡しません。

と書きました。
その部分にリダイレクト設定を渡します。
`/maintenance.html`へのアクセス時に`/`にリダイレクトさせる処理を渡しましょう。

```diff_javascript:next.config.js
             permanent: false,
           },
         ]
-      : [];
+      : [
+          {
+            source: "/maintenance.html",
+            destination: "/",
+            permanent: false,
+          },
+        ];
   },
 };
```


# おわりに

ここまでで、Next.jsのアプリケーションにメンテナンスモードを入れることができました。
環境変数を渡しているため、メンテナンスモードに切り替えるためには再度デプロイが必要な点にご注意ください。

[Ateam Commerce Tech Inc. Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ateam-commerce-tech)の次回の記事は、@kazzzzzz による「Hasura&Nextjsで快適な開発環境を構築〜プロダクション環境構築編〜」です。お楽しみに 🕺