こんにちは、SaaS プロダクト開発部テックリードの丸山です。
2023 年 10 月 26 日に、Next.js の最新バージョンである Next.js 14 がリリースされました。
そこで早速、Next.js 14 で作ったウェブサイトを AWS Amplify でホスティングしようとしたところ、想定外に苦戦しましたので、備忘録がてらブログの記事に残しておこうと思います。
なお、本記事は 2023 年 11 月 2 日時点での AWS Amplify の仕様に基づいて執筆しております。今後の AWS Amplify のアップデート等により、状況は変わる可能性が十分にありますので、ご留意ください。
- 結論(お急ぎの方へ)
- AWS Amplify とは
- Next.js 14 アプリを作成する
- Amplify でホスティングしてみる
- Framework を Next.js - SSG に変更してみる
- [参考] Amplify による Framework の自動判定
- まとめ
結論(お急ぎの方へ)
AWS Amplify で Next.js 14 アプリを SSG としてデプロイするには、以下の 4 点が必要です。
Next.js 側の設定
next.config.js
にoutput: 'export'
を追加する
Amplify 側の設定
- ビルド設定 YAML の
artifacts.baseDirectory
の値を.next
からout
に変更する - ビルドイメージをデフォルトから
public.ecr.aws/docker/library/node:18
に変更する - アプリ作成後に以下のコマンドを実行して platform と framework の値を上書きする
aws amplify update-app --app-id <APP_ID> --platform WEB aws amplify update-branch --app-id <APP_ID> --branch-name main --framework 'Next.js - SSG'
AWS Amplify とは
AWS Amplify については検索すれば情報が色々と出てきますので、詳細は割愛させていただきますが、簡単に言えば、フロントエンドの開発者が、AWS に関する詳しい知識がなくてもバックエンドまで含めたアプリケーションを簡単に開発およびデプロイできるようにするフレームワーク であり、Amplify CLI や Amplify Hosting、Amplify Studio など複数のツールから構成されています。
今回はその中でも、Next.js で作ったアプリケーション(静的サイト)をデプロイするために、Amplify Hosting の機能のみを使用します。Amplify Hosting はその名前の通り、ホスティングの機能をもったサービスですが、GitHub などのリポジトリと組み合わせることで CI/CD による自動ビルド、自動デプロイが可能です。なお、同様の機能を持ったサービスとして Vercel や Netlify などが知られています。
Next.js 14 アプリを作成する
それでは早速 Next.js 14 アプリを作成していきましょう。
npx create-next-app@latest
すべてデフォルトの設定で作成していきます。
✔ What is your project named? … my-app
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to usesrc/
directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
yarn run dev
を実行すると http://localhost:3000 で Next.js アプリが立ち上がります。
今回はページ内に動的なコンテンツがないため、ページ表示時間の高速化の観点から、SSG でホスティングすることにします。SSG (Static Site Generation) とは、事前に静的な HTML/CSS/JavaScript を生成しておく方式のことで、SSR (Server Side Rendering) のように、リクエストを受け付けてから HTML を生成する方式とは対照的な考え方になります。
SSG を有効化するため next.config.js
に output: 'export'
を追加します。
next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { output: 'export' } module.exports = nextConfig
なお、Next.js 13.2 以前のバージョンでは next export
コマンドにより静的なアセットを出力できたため、以下のように package.json の script
に記述しておくことが一般的でした。
package.json(Next.js 13.2 以前)
"scripts": { "build": "next build && next export" }
Next.js 13.3 以降は、next export
コマンドは非推奨となり、代わりに next.config.js
で設定する方法が推奨されています。そして Next.js 14 では next export
コマンドは完全に削除されています。*1
ここまでできたら、ソースコードを GitHub にアップロードしておきます。
Amplify でホスティングしてみる
作成した Next.js 14 アプリをホスティングするため、Amplify Hosting 側の設定を始めます。
最初に Git リポジトリの場所を選択します。先ほどソースコードを GitHub にアップロードしておいたので、ここでは GitHub を選択します。
続いて、連携する GitHub リポジトリを選択します。
続いて、アプリケーションのビルドに関する設定を行います。Next.js 14 で SSG を有効化している場合、out
という名前のディレクトリに静的な HTML/CSS/JavaScript が出力されるため、artifacts の baseDirectory を out
に書き換える必要があります。
そしてこの次が最初のハマりポイント。Amplify がデフォルトで使用するビルド環境には、複数のバージョンの Node.js がインストールされているのですが、2023 年 11 月 2 日現在、最新のバージョンは Node.js 18.13 です。Next.js 14 は Node.js 18.17 以上を要求するため、yarn install
実行時にエラーが発生してしまいます。そのため、ビルド環境のイメージをデフォルトから変更し、public.ecr.aws/docker/library/node:18
*2 など Node.js 18.17 以上がインストールされているイメージを指定する必要があります。
次の画面に進むと、何かがおかしいことに気づきます。Framework のところに Next.js - SSR と表示されています。ここは、ソースコードの内容を元に Amplify 側で自動的に判定されるのですが、実際のソースコードでは SSR ではなく SSG になるように設定しているので、自動判定が間違っています。右上にある「Edit」ボタンを押しても、この Framework の値を変更することはできません。
このままデプロイに進むと、ビルドの過程で以下のエラーが発生してデプロイに失敗します。
2023-11-03T09:59:47.078Z [ERROR]: !!! CustomerError: Build output not found in /codebuild/output/src2076943348/src/next14-test/amplify-compute-bundle-output/compute/.next/server, please check your build artifacts path
これは、Amplify 側が SSR 向けの出力を期待しているのに対し、実際に build スクリプトの実行後に out
ディレクトリに出力されるのは SSG 向けの静的ファイルなので、期待したファイルが出力の中に見つけられず、build に失敗したと判定されているわけです。
Framework を Next.js - SSG に変更してみる
Amplify の管理画面上では、一度自動判定された Framework の値を変更することはできませんが、実は AWS CLI を使用することで、Framework の値を変更することができます。*3 変更するには、以下のコマンドを実行します。
aws amplify update-app --app-id <APP_ID> --platform WEB aws amplify update-branch --app-id <APP_ID> --branch-name main --framework 'Next.js - SSG'
APP_ID
は、アプリケーションごとに割り当てられている ID で、aws amplify list-apps
コマンドなどで確認することができます。上記ではまず、アプリケーションの platform の値を WEB_COMPUTE
から WEB
に変更しています。次に、framework の値を Next.js - SSR
から Next.js - SSG
に変更しています。
上記のコマンドを実行してから、再デプロイを行えば、無事にデプロイに成功するはずです。
[参考] Amplify による Framework の自動判定
そもそも、Amplify によって Framework が正しく自動判定されていれば、今回のように AWS CLI を使って Framework の値を更新する必要もありません。では、Amplify はどのようにして、ソースコードから SSR か SSG かを判定しているのでしょうか。
Amplify の ドキュメント に以下のような記述があります。
When you deploy a Next.js app, Amplify inspects the app's build script in the package.json file to detect whether the app is static (SSG) or server-side rendered (SSR).
つまり、package.json の build スクリプトの記述を見て SSR か SSG かを判定しているようです。
本記事の前半で説明した通り、Next.js 13.2 以前のバージョンでは、SSG を行うために以下のように package.json の script
に記述していました。
package.json(Next.js 13.2 以前)
"scripts": { "build": "next build && next export" }
SSG の場合は next build && next export
と記述し、SSR の場合は next build
とだけ記述していました。そのため、Amplify は next export
という記述が package.json の build スクリプトに含まれているかどうかで、SSR か SSG かを判定するという仕様のようです。
ところが、Next.js 13.3 以降 next export
は非推奨となり、Next.js 14 では完全に削除されました。つまり、この Amplify の判定方法は 最新の Next.js では通用しない というわけです。
逆に、この仕様を逆手に取ることで、以下のような方法で SSG と認識させることも可能です。
package.json
"scripts": { "build": "next build && next export", "build2": "next build" }
package.json の build スクリプトに next build && next export
と書いておき、それとは別に「本物の」build スクリプトとして build2 というスクリプトを用意しておきます。Amplify のビルド設定 YAML で、実際のビルド時に実行されるコマンドとして npm run build2
と書いておけば、実際に実行されるのは next build
だけですが、Amplify の Framework 自動判定には Next.js - SSG
と判定させることができます。このような小細工を package.json にしておくことで、わざわざ AWS CLI を使って Framework を更新せずとも一発で Amplify にデプロイできるようになります。
まとめ
Next.js のホスティングには Vercel を使いましょう(笑)
今回 Amplify を触ってみて、最新の Next.js の仕様に追従できていないことが明らかになりました。Vercel は Next.js の開発元である Vercel 社が提供するサービスですので、Next.js との親和性は間違いなく最高です。Vercel は商用利用の場合はユーザーごとに最低 $20 /月の料金がかかるのがネックですが、今後サービスを選定する際には、最新バージョンへの対応速度という観点も気にしておきたいところです。
*1:https://nextjs.org/docs/app/building-your-application/deploying/static-exports#version-history
*2:node:18 を指定すると Docker Hub の pull 回数制限に引っかかるため、ECR Public 上で公開されている Docker オフィシャルイメージである public.ecr.aws/docker/library/node:18 を指定しています。参考:https://dev.classmethod.jp/articles/docker-official-images-on-ecr/
*3:https://github.com/aws-amplify/amplify-hosting/blob/main/FAQ.md#convert-an-ssr-app-to-ssg