はじめに
Next.jsのSSG(Static Site Generation)で作成したWebアプリケーションをAWS Amplifyにデプロイする際、いくつかハマりポイントがありました。ネットで調べてもPage Router時代の古い情報は多く、App Routerの場合の情報が少なかったため、忘れないよう記事にしておこうと思います。
フレームワーク・ライブラリ等のバージョンは執筆時点でのlatestもしくはstableなバージョンを利用しています。参考にされる際はこの点ご留意ください。
SSGとは
Static Site Generationの略です。Next.jsで実装したアプリケーションをnext build
でビルドする際に静的なコンテンツ(HTML, CSS)に落とし込み、それらをCDNでキャッシュして配信することで高速なレスポンスを実現できます。静的コンテンツと聞くと利用シーンが限定されそうに聞こえますが、外部へのデータフェッチを行うアプリケーションであってもSSGを利用することができます。(ソースコード側で少し工夫は必要)
https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation
ハマりポイント
next.config.jsの設定
Next.jsのレンダリング方式は、デフォルトではSSRが適用されています。これをSSGにするためにはnext.config.jsに追記が必要です。
/** @type {import('next').NextConfig} */
const nextConfig = {
+ output: "export",
+ images: { unoptimized: true },
};
export default nextConfig;
まずoutput: "export",
ですが、これを設定した状態でnext build
でビルドを行うと、outというディレクトリに成果物が生成されます。outには静的アセットのみが出力されており、これらをWebサーバーに置けばそれだけで配信が可能になります。next.jsのリポジトリを確認したところ、以下の記述がありました。
/**
* The type of build output.
* - `undefined`: The default build output, `.next` directory, that works with production mode `next start` or a hosting provider like Vercel
* - `'standalone'`: A standalone build output, `.next/standalone` directory, that only includes necessary files/dependencies. Useful for self-hosting in a Docker container.
* - `'export'`: An exported build output, `out` directory, that only includes static HTML/CSS/JS. Useful for self-hosting without a Node.js server.
* @see [Output File Tracing](https://nextjs.org/docs/advanced-features/output-file-tracing)
* @see [Static HTML Export](https://nextjs.org/docs/advanced-features/static-html-export)
*/
https://github.com/vercel/next.js/blob/canary/packages/next/src/server/config-shared.ts#L884
次にimages: { unoptimized: true },
ですが、これは「画像の最適化を行わない」という設定になります。
画像の最適化、というのはnext/image
の<Image>
タグを用いた最適化のことを指しています。<Image>
タグはHTMLの<img>
を拡張したもので、デバイスごとに画像サイズを最適なものに変更したり、画像読み込みのタイミングを調整したりと、裏で色々してくれます。
https://nextjs.org/docs/pages/building-your-application/optimizing/images
https://nextjs.org/docs/pages/api-reference/components/image#unoptimized
しかし、この最適化処理はサーバーで行われます。SSGはnext build
実行時に生成された静的アセットを配信するため、ページ表示時にサーバーで行われる処理を実施することはできない?ようです。SSGなページで<Image>
を使うと、画像が読み込まれませんでした。loaderを変更することで解消できるとの記事も確認しましたが、今回はそこまでするモチベーションはなかったので、単純に最適化をしない設定にすることで回避しました。
https://zenn.dev/kisukeyas/scraps/6fd8bf7e63e4a3
https://ebisu.com/note/next-image-ssg/
amplify.ymlの修正
$frontend.artifacts.baseDirectory
を.next
にする必要がありました。
frontend:
artifacts:
- baseDirectory: out
+ baseDirectory: .next
SSGの成果物はoutディレクトリに出力されるのでoutを指定するものだと思っていたのですが、どうやらそうではないみたいです。後述しますが、Next.js v14以降を利用したSSGアプリケーションをホスティングする場合、Amplfy側にはSSRアプリケーションの静的コンテンツとして配信する必要があるみたいです。
Amplify Hostingの設定
Amplifyにアプリをデプロイすると、Amplify側でアプリケーションのフレームワーク
とプラットフォーム
を自動で検出してくれます。
ここが個人的に混乱した箇所なのですが、フレームワークをNext.js - SSR
で設定しないといけないようです。
なぜSSGなのにSSRで設定するのか?となりますが、どうやらSSRアプリケーション内の静的コンテンツとしてindex.htmlが配信される形になってるみたいです。実際、ブラウザの開発者ツールで確認したところ、_next/static
配下に成果物が保存されていたので、静的コンテンツとして認識されていることが確認できます。
プラットフォーム
はWEB_COMPUTE
を設定します。通常、Amplifyで静的コンテンツを配信する際はWEB
を指定するようですが、Next.js v14以降を利用したSSGアプリケーションはWEB_COMPUTE
を使ってくださいとの記述がAWS CLIの公式ドキュメントにありました。
If you are deploying an SSG only app with Next.js version 14 or later, you must set the platform type to WEB_COMPUTE .
ちなみに、フレームワークをNext.js - SSG
、プラットフォームをWEB
に、amplify.ymlのbaseDirectoryをout
にしてデプロイしたところ、_next/static
にコンテンツが保存されてました。まとめると、以下2パターンのどちらかであればデプロイに成功するっぽい?
- パターン1
- framework:
Next.js - SSG
- platform:
WEB
- amplify.yml: $frontend.artifacts.baseDirectory:
out
- framework:
- パターン2
- framework:
Next.js - SSR
- platform:
WEB_COMPUTE
- amplify.yml: $frontend.artifacts.baseDirectory:
.next
- framework:
余談ですが、フレームワーク
とプラットフォーム
はGUIから変更できないため、変更が必要な場合はCLIから実行する必要があります。
# change platform
$ aws amplify update-app --app-id <value> --platform WEB_COMPUTE
# change framework
$ aws amplify update-branch --app-id <value> --branch-name <value> --framework 'Next.js - SSG'
SSGなのにSSRを指定する、というのはユーザーの混乱を招く仕様なので、いずれ仕様変更されるんじゃないかと想像してます。
根本の原因としては、Next.jsがv14からビルドコマンドの仕様を変更したことが起因してるようです。以下の記事がとても参考になりました。
https://zenn.dev/ototrip/articles/tech-nextjs-amplify-4
さいごに
いつもNext.jsを使う際はSSRのことばかり考えているので、SSGを使うのは初めてでした。正直さくっとできるかなと高を括ってましたが、細かいところで色々ハマったのでいい勉強になりました。