yasutomogのブログ

Software Engineerの雑記

Amplify + Next.js + microCMSのプレビュー環境について

概要

株式会社トライビート| TRIBEAT CO., LTD. では、数年前からJamstackなWebサイトやECサイトの構築を推進(提案)しており、年々こういったご相談を受けることが増えています。 選定技術はそのときの顧客要件によって変わりますが、Amplify、Next.js、microCMSを使うことが多いです。Jamstackな構成を検討していると、SSGしてファイル生成する都合上、プレビュー機能をどうする?って課題に当たります。Headless CMSで下書きしたとき(公開前)に、Webページとしてどう見えるかを確認するユースケースですね。

アーキテクチャ的にSSGしたものをCDNホスティングすることで、「高速化、スケーラビリティ、セキュア」を担保する一方で、リアルタイムな表示は工夫が必要になります。プレビュー用のページもSSGすれば確認可能ではありますが、ビルドには大抵数分掛かるため、UXが悪くなり実用的ではないと考えています。

Next.jsでは標準的にPreview Modeが用意されていますが、昨年の調査時はAmplify上で正しく動作しませんでした。最近、Amplify hostingのGitHub issueを確認したところ、解決されていそうだったため、改めて検証してみました。

Next.js のPreview Modeとは

nextjs.org

上記の公式ドキュメントに実装方法が手順付きで記載されているので、詳細はこちらを確認するのが良いですが。色々端折って要約すると、Next.js には元々、APIを追加する機能が備わっており、 そのAPIの中で、 setPreviewData 関数を利用することで、プレビュー機能に必要なCookieが発行されます。 このCookieを保持しているブラウザーからアクセスされた場合、SSGで生成済みのページを返すのではなく、 getStaticProps というビルド時に実行されるページ生成用の関数が呼び出されます。

上記機能を利用することで、以下のようなフローが実現可能となります。Headless CMSからプレビュー用URLがリクエストされたときに、Cookieを発行して、対象のページにリダイレクトすると、新たにページ生成した内容で確認が可能となります。

Cookieを保持しているかどうかは、 getStaticProps のパラメーターにある context.preview のbool値で判断可能となります。Cookieを発行する手前の処理で、認証処理を実装することで、Headless CMSからアクセスされたときのみ、機能が有効になるように制御します。

Amplifyでは何が課題だったのか

github.com

github.com

1つ目のissueに関連付けられている、2つ目のissueで解決したようです。Next.jsのPreview Modeは、HTTP Response Header で Cookieを発行していますが、Amplify上にデプロイすると、これがうまく動作していなかったようです。解決されたのが、2022年の12月ということです。

実運用での課題

Amplify上でNext.jsのPreview Modeが動作するということで、課題はなくなったように思えますが、ページ更新のユースケースを想定すると、若干の工夫や考慮が必要になります。当初、本番(hosting)用のAmplify環境(env)上でプレビュー機能も動作させれば、環境構成もシンプルになり最適と考えてましたが、以下のようなケースで使いにくくなると想像しています。

前提

ニュースやブログなどを扱うWebサイトを(Cookieとかはよくわからない)非エンジニアの方がコンテンツ管理している場合

ケース1

  1. ニュースの下書きデータを Headless CMS からプレビュー機能で確認(このときにPreview Mode の Cookie が発行)。
  2. その後、別な作業をしている時に、Webサイトから現在のブログ内容を確認。
  3. 担当者は、公開中のブログページ内容を確認するつもりだが、意図せず下書きデータが保存されていると、下書き内容でページが表示される。

ケース2

  1. 既に公開済みの記事に不備があり、Headless CMSからデータ編集し、プレビュー機能で確認(このときにPreview Mode の Cookie が発行)。
  2. 現在公開中のページと更新後のページを目視で差分チェックしようとしたところ、下書き内容のページでしか確認ができない

上記のようなケースは、Cookieを保持しているブラウザーはSSGされたものではなく、常に新しくページ生成した結果が表示されるため発生します。以下の図のAとBは、同じサイトページをリクエストしていますが、Cookieの保持により表示されるコンテンツ内容は異なる可能性があります。 CMSの操作や現状サイトの確認などを繰り返し作業しているときに、Cookieの保持について意識するのは厳しいと想像しています。

Preview ModeのCookieを発行する setPreviewData 関数は、引数なしで使用するとSession Cookieとなります(ブラウザが閉じられると、Cookieが削除されます)。引数を指定することで、Cookieの有効期間(Expired)を設定することも可能ですが、最近のPCやMacの使われ方だと、ブラウザーを閉じないでアクセス作業することも多いと想像しています。 また、Next.jsでは、Preview ModeのCookieを削除する関数も用意されていますが、何をトリガーに呼び出すべきかは悩ましいです。

上記から、Amplifyプロジェクト内で、本番用とプレビュー用に環境(env)を分けて構築しようと思いましたが、同じGitブランチを別の環境(env)に分けることは想定されていないようで、Amplify Console上で環境(env)を追加するときに、既に接続済みのGitブランチは表示されないように制御されていました。

どうすべきか悩みましたが、本番用とプレビュー用でAmplifyプロジェクトを分けてしまい、同じGitブランチを接続させるのが、いまのところ良さそうです。microCMSなどのHeadless CMSからリクエストする先を、プレビュー用のAmplifyプロジェクトへ向けることで、本番環境の表示とプレビュー環境の表示を切り分けれます。

プレビュー用のAPIについて

blog.microcms.io

Next.jsの公式ドキュメントもわかりやすかったですが、microCMSのブログも丁寧な内容で助かりました。特に、 getStaticPathsfallback の設定変更については、公式ドキュメントに記載がないので参考になりました。 あまり大きな問題ではないですが、以下は今後検討して作り込む予定です。

  • microCMSのdraftKeyについては、Webhookのクエリストリングで渡すべきか、Amplify環境変数で設定すべきか?
  • プレビュー用のAPIについては、各ページごとに作るべきか、まとめるべきか?

AmplifyのSSR対応時のビルドについて

Amplifyは内部で、Next.jsのアプリケーションがSSGまたはSSRを判断して環境構築を変えているようです。Amplify + Next.jsの構成を最初に試したときは、2〜3年前でした。その当時、SSGモードではビルドが数分で終わるものが、SSRモードに変わることで、20〜30分掛かる事象にあたりました。今回のプレビューAPIは、SSRモードでなければ動作しないこともあり、ビルド時間に懸念がありましたが、AmplifyもNext.jsに合わせて進化しており、いまのところ、SSGとSSRでビルド時間はほぼ変わらないことを確認済みです。