前回まで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の赤。どれも手元にある表示でした。
読み方を知らないと、目の前にあっても素通りします。
逆に一度読めるようになると、次からは同じ場所が答えを教えてくれる。
今のところ、そういう手応えでやっています。
