Next.jsの「Dynamic Server Usage」エラーに翻弄された話。
先週、チームメンバーから「ビルドが通らないんですけど...」というSlackが飛んできました。
「え、私の環境ではビルド通ってるんだけど・・・?」
事の発端
私たちのチームでは、Next.js 15を使ってBtoBのSaaSを開発しています。ある日、新しくジョインしたメンバーが開発環境をセットアップしていたところ、yarn build
でエラーが発生。
Failed to get auth token: Error: Dynamic server usage: Route /mypage couldn't be rendered statically because it used `cookies`.
不思議なことに、私の環境では同じコマンドを実行してもビルドは成功。エラーメッセージは出るものの、最終的には正常にビルドが完了していました。
「とりあえずnode_modulesを削除して、yarn installし直してみて」
定番の解決策を提案しましたが、状況は変わらず。ここから本格的な調査が始まりました。
まず疑ったこと
最初に確認したのは、お決まりの環境差分です。
- Node.jsのバージョン → 同じ(Voltaで統一済み)
- 環境変数 → 同じ(.env.localも共有済み)
- ブランチ → 同じ(developブランチ)
「うーん、なんで?」
真犯人を見つけるまで
「そういえば、APIサーバーは起動してる?」と聞いてみました。
「あ...起動してないです」
まさかと思いましたが、これが原因でした。
私たちのマイページでは、ユーザー情報を表示するために以下のような処理をしていました(抜粋):
// マイページのテンプレート
export async function MyPageTemplate() {
const user = await getMe(); // APIサーバーから情報取得
return (
<div>
<h1>ユーザー情報</h1>
<p>名前: {user?.name}</p>
</div>
);
}
ビルド時に、Next.jsはこのページを静的に生成しようと試みます。その過程でgetMe()
が実行され、APIサーバーにアクセスしようとしますが、サーバーが起動していないため接続エラーが発生。これがビルドを失敗させていました。
でも、なぜ私の環境では動いていたのか?
私がメイン実装者なので常にAPIサーバーを起動した状態で開発していました。
今回、お手伝いを依頼したメンバーは静的ページを担当してもらっており、DockerによるAPIサーバーのローカル環境構築を見送っていました。また、Amplifyでのビルドも成功していたのは、ビルド時にAPIのURLが本番環境を指していたからです。
つまり、エラーメッセージの「Dynamic server usage」は本質的な問題ではなく、APIサーバーへの接続エラーが真の原因だったのです。
Next.jsの挙動を理解する
この問題を通じて、Next.js 15の興味深い挙動が分かりました。
Next.jsは、ビルド時に各ページをどのようにレンダリングするか判断します。基本的な流れはこうです:
- まず静的生成(SSG)を試みる
-
cookies()
やheaders()
などの動的関数を検出 - 動的レンダリング(SSR)に切り替える
私たちのケースでは、認証情報を取得するためにcookies()
を使用していました。本来であれば、Next.jsはこれを検出して自動的に動的レンダリングに切り替えるはずです。
しかし、APIサーバーが起動していない環境では、その前に接続エラーで処理が中断されてしまっていました。
解決策:明示的に動的ページであることを宣言
この問題を根本的に解決するため、認証が必要なページには明示的に「これは動的ページです」と宣言することにしました。
// (authenticated)グループのlayout.tsx
export const dynamic = 'force-dynamic';
export default function AuthenticatedLayout({ children }) {
return (
<div>
<Header />
<main>{children}</main>
</div>
);
}
この一行を追加することで、Next.jsは最初から動的レンダリングを選択し、ビルド時の無駄な静的生成の試行を避けられます。
学んだこと
今回の経験から、いくつか重要な教訓を得ました。
1. エラーメッセージを鵜呑みにしない
「Dynamic server usage」というエラーに気を取られすぎて、本当の原因(APIサーバー未起動)に気づくのが遅れました。エラーメッセージは手がかりの一つですが、全体の文脈を見ることが大切です。
2. 開発環境の前提条件を明文化する
READMEに「ビルド時はAPIサーバーの起動が必要」と書いておけば、この問題は防げたはずです。当たり前だと思っていることこそ、ドキュメント化が必要です。私のワンマン開発だったんでこれは盲点というか、二の次になってたことは反省です。
3. Next.jsのデフォルト動作を理解する
Next.jsは「できるだけ多くのページを静的に生成したい」という思想で動いています。これは多くの場合で有益ですが、認証が必要なアプリケーションでは明示的な制御が必要になることがあります。
おわりに
「なんで私の環境では動くんだろう?」という疑問から始まった調査でしたが、結果的にNext.jsの動作原理をより深く理解する良い機会になりました。
同じような問題で悩んでいる方の参考になれば幸いです。そして、もし皆さんのチームで「ビルドが通らない」という声が上がったら、まず「APIサーバー起動してる?」と聞いてみてください。意外とそれが答えかもしれません。
Discussion