開発サーバーと本番ビルドが、同じ言葉で別のことを言っていました

前回まで3本、ビルドが何をしているかの話を書いてきました。
最後は、その作業中に2日続けて引っかかったものです。

どちらも表示を読み違えたのですが、間違え方が同じ形でした。

  • 1日目: dev が Static と言っているのを「静的化できている」と読んだ
  • 2日目: dev が Compiled と言っているのを「ビルドもされている」と読んだ

語は合っています。指しているものが違いました。

目次

1日目: 同じ「Static」が、別の問いに答えていた

Next.js の開発サーバーには、画面左下に丸い「N」のインジケーターがあります。
これを開くと、そのページの Route info が見られます。

作業中のページを開いたら Static と出ていました。
静的生成できているのか、と思いました。

ところが前日に走らせた npm run build の出力はこうです。

ƒ /[username]

ƒ は Dynamic の印です。
同じページについて、dev は Static と言い、build は Dynamic と言っている。

見ている問いが違いました。

何を判定しているか
dev の Route info このページが動的API(cookies() など)を使っているか
build の ƒ ビルド時にHTMLを作れたか

このときのページは、表示するデータをハードコードした配列から取っていました。
動的APIを使っていないので、dev は Static と答えます。

一方ビルド時には、/guest というパスが存在すること自体を Next.js が知りません。
動的セグメントのパス一覧は generateStaticParams がないと分からないので ƒ になります。

どちらも正しく答えていました。質問が違っただけです。

2日目: dev には「焼いてある」状態がなかった

翌日、npm run dev の出力を見てこう思いました。

✓ Compiled /[username] in 1.2s

コンパイルしている。ならビルドもしているのでは。

静的化の作業中だったので、npm run build を毎回回さずに dev で確認できるなら楽だ、
と思いたかったのもあります。

3つのコマンドを並べたら、仕事が違いました。

コマンド 何をするか
next dev アクセスされたページだけをその都度コンパイル。HTMLを書き出さない
next build 全ページをコンパイルして最適化し、静的HTMLを書き出す
next start build の成果物を配信する(build なしでは動かない)

dev には「先に焼いてある」という状態が存在しません。
だから generateStaticParams の成果を dev で再現することは、原理的にできませんでした。

このとき確認したかったのは、/matsutake/guest の2本が個別に焼けているかどうかです。
これは npm run build の出力に ● /matsutake ● /guest と並んで初めて分かります。

紛らわしいのは、generateStaticParams 自体は dev でも呼ばれていることです。

generate-params: 827ms

ログに出ます。呼ばれてはいる。焼かれてはいない。

動いている証拠のように見えるものが、確認したいこととずれていました。

なぜ気づけないのか

2件をまとめて書いているのは、ここが同じだからです。

語が同じなので、読み替えているという自覚が持てません。

知らない単語なら調べます。
知っている単語だから、そのまま自分の文脈で読んでしまう。

「Static」も「Compiled」も、dev と build では別のレイヤーの話をしていました。
それぞれの計器が何を測っているかを知らないまま素直に読むと、読んだぶんだけ外します。

計器は目的で選ぶことにしました

使い分けを決めました。

確認したいこと 見る場所
動的APIを使ってしまっていないか dev の Route info
静的化できたか build の出力( ƒ

この工程には、認証用のクライアント(cookies() を呼ぶもの)を公開ページで import すると
静的化が永久に失敗する、という罠があります。しかもエラーは出ません。

dev の Route info は、この罠をビルドを待たずに検知できます。
DBを読むコードを書いたあとに左下の「N」を見て、
Dynamic に変わっていたら import 先を間違えている、という使い方です。

逆に Static のままでも、静的化できた証明にはなりません。判定の正はビルド出力のほうです。

壊れたことは即座に分かるけれど、直ったことは分からない計器。
そう思って見るようにしました。

おわりに

このシリーズでは、ビルドが何をしているかを4本に分けて書いてきました。

use server は実行場所ではなく公開範囲の宣言でした。
"use client" を含むページは静的に焼けていました。
静的に焼くと決めた瞬間、ビルドが外部サービスに依存しました。
そして dev と build は、同じ言葉で別のことを言っていました。

4本とも、自分の思い込みを崩したのは他人の説明ではありません。
ビルド出力、Network タブ、CIの赤。どれも手元にある表示でした。

読み方を知らないと、目の前にあっても素通りします。
逆に一度読めるようになると、次からは同じ場所が答えを教えてくれる。
今のところ、そういう手応えでやっています。

目次