← ミライズ労務コンサルティング開発知識シリーズ 目次(全6部)
第 4 部
hokama@mirise ~/miraise-crm $ node --version

Node と Next.js の内側

第2部では「何であるか」を見ました。ここでは「どう動いているか」を見ます。
挙動の理由がわかると、AI が出したコードの誤りを見抜けるようになります。

この資料の読み方
  • 各章は要点の先出しから始まります。まず3〜4行だけ読めば、その章が何の話かはわかります
  • 難しい章には点線で囲んだたとえ話を置いています。抽象的な説明で詰まったら、そこまで飛んで構いません
  • 専門用語は初出時に日常の言葉で言い換えています。用語そのものを暗記する必要はありません
  • 通読を前提にしていません。詰まった箇所を引くための資料として使ってください
$ node -e "console.log(1)"

Node はなぜ1本のスレッドで動くのか

Node の挙動のほぼすべてが、この一点から説明できます。

この章の要点
  • Node は1人分の手しか持っていない。同時に2つの作業はしない
  • それでも大勢をさばけるのは、待ち時間に別の仕事を差し込んでいるから
  • だから待ち時間の多い仕事に強く、手を動かし続ける仕事には弱い
たとえ話

職員が1人しかいない事務所

A社の件で、年金事務所へ照会を出しました。返事が来るのは30分後です。

従来型のやり方は「電話の前に座って30分待つ」です。その間、B社もC社も手つかずになります。3件を同時に進めたければ、職員を3人雇うしかありません。

Node のやり方は「照会を出したら、すぐB社の書類に取りかかる」です。年金事務所から連絡が入ったら、そこで手を止めてA社の続きに戻る。職員は1人のまま、3件が並行して進みます。

ただし手は1組しかありません。3時間かかる集計を始めたら、その間はB社もC社も一切進みません。照会と違って、集計は自分の手が塞がるからです。これが「重い計算に弱い」ということの中身です。

Node は JavaScript を1本のスレッドで実行します。スレッドとは「作業を進める筋道」のことで、1本しかないというのは、上のたとえでいう職員が1人という状態にあたります。つまり、同時に2つの処理を走らせていません。

にもかかわらず、何千という同時接続をさばけます。なぜか。待つのをやめたからです。

従来型 ─ 待っている間、そのスレッドは止まる 問い合わせ 待機(何もしない) 結果を処理 Node ─ 待たずに次の仕事へ行く 問い合わせ 別の依頼を受ける さらに別の依頼 結果を処理 時間 →
経過時間は同じです。違うのは、灰色の空白を仕事で埋めたかどうかだけです。

従来型のサーバーは「データベースに問い合わせて、返事を待つ」間、そのスレッドが停止します。同時に100人来れば、スレッドを100本立てることになります。

Node は違います。問い合わせを出したら、返事を待たずに次の仕事へ行きます。返事が来たら「終わりました」と通知が入り、そこで初めて続きを処理する。この方式を非同期(asynchronous)と呼び、通知を受け取って順番に処理する仕組みをイベントループと呼びます。

料理人にたとえるなら、湯を沸かしている間に別の下ごしらえをする一人の料理人です。人を増やしたのではなく、待ち時間を使い切っている。

この設計が決めていること

仕事の性質Node の適性理由
待ち時間が長い(DB、API、ファイル、通信)非常に得意待ち時間を他の仕事で埋められる
CPU を長時間使う(画像変換、大規模計算)苦手1本しかないスレッドを占有してしまう

後者が重要です。重い計算を始めると、その間すべてのリクエストが止まります。1本のスレッドが塞がるからです。Web サーバーとしては致命的になりえます。

「Node は速い」「いや遅い」という議論が噛み合わないのは、両者が別の仕事の話をしているためです。仕事の性質を指定しないと、どちらの主張も正しくなります。

重い処理が必要になったら

逃げ道は3つあります。別プロセスに出す(Python スクリプトを呼ぶなど)、worker_threads を使う(Node に別スレッドを立てる仕組みがあります)、外部サービスに委ねる

第3部で見た「Excel の処理は Python で」という判断も、実はこの延長線上にあります。適材適所を、言語ではなく仕事の性質で決める、ということです。

$ grep -rn "await" src/

async / await の正体

前章の非同期を、コード上で表現するための記法です。

この章の要点
  • await は「止まる」ではなく「順番を譲る」
  • async が付いた関数は、結果ではなく引換券を返す
  • forEach の中の await は効かない。ここだけは覚える価値がある
たとえ話

クリーニングの引換券

服を預けると引換券をもらいます。券そのものは服ではありません。「あとで服と交換できる」という約束です。

async が付いた関数は、必ずこの引換券を返します。中身がまだ無くても、券はその場で即座に出ます。この券のことを Promise と呼びます。

await は、その券を出して実物を受け取る操作です。ただし窓口で突っ立って待つのではありません。「できたら呼んでください」と言って、他の用事を済ませに行く。呼ばれたら戻ってくる。

前章の「照会を出したら別の仕事へ行く」が、コードの上ではこの形で書かれます。

まず言葉から。await は「待つ」と読めますが、実際の意味は「ここで一旦、他の仕事に順番を譲る」です。スレッドを止めているのではありません。

// この関数は「途中で中断できる関数」になる async function 給与を取得(id) { const 従業員 = await db.query('SELECT ...'); // ここで順番を譲る const 給与 = await db.query('SELECT ...'); // 結果が来たら再開し、また譲る return { 従業員, 給与 }; }

async が付いた関数は、必ず Promise というものを返します。Promise は「結果はまだ無いが、いずれ出る」という引換券のようなものです。await は、その引換券を実物と交換する操作にあたります。

歴史を知ると読める

世代書き方問題
コールバック関数を引数として渡し、終わったら呼んでもらう入れ子が深くなり読めなくなる
Promise.then() でつなぐまだ縦に長い
async / await上から下へ普通に書ける─(現在の標準)

古い記事に出てくる .then() の連鎖や、関数を引数に渡す書き方は、すべて同じことを別の記法で表現しています。中身は変わっていません。

最も多い間違い

AI が生成したコードでも実際に出ます。知らないと絶対に気づけない種類のバグです。

動かない例
// 待ってくれない。全件の処理が終わる前に次へ進む 従業員リスト.forEach(async (人) => { await 給与を計算(人); }); console.log('完了'); // ← 実際にはまだ完了していない

forEach は中の関数が非同期かどうかを見ていません。渡された関数を呼びっぱなしにして、次へ進みます。

なぜそうなるのか。forEach は「渡された作業を順に呼び出す」だけの仕組みで、非同期という考え方が JavaScript に入る前に作られました。だから中の関数が引換券を返しても、それを見ずに捨てます。「呼んだから終わり」と判断して、次の1件へ行ってしまう。

たとえでいえば、8人分のクリーニングを預けて引換券を8枚受け取り、その券を全部その場に置いたまま帰った状態です。服は受け取れていません。

// 正しい ─ 1件ずつ順番に待つ for (const 人 of 従業員リスト) { await 給与を計算(人); } // 正しい ─ 全件を同時に走らせて、全部揃うまで待つ await Promise.all(従業員リスト.map(人 => 給与を計算(人)));

下の Promise.all は、前章の非同期の利点をそのまま使う書き方です。100件のデータベース問い合わせを1件ずつ待てば100件分の時間がかかりますが、まとめて投げれば、最も遅い1件の時間で済みます。

検算の観点

AI が書いたコードを読むとき、async が付いている関数の呼び出しに await があるかを確認してください。付け忘れると、エラーも出ずに「途中の状態で先に進む」という、最も追いにくい不具合になります。

TypeScript を使い、リンタを入れていると、この種の付け忘れは機械が指摘してくれます。第2部で TypeScript を勧めた実質的な理由がこれです。

$ cat package.json | grep type

モジュール方式の2系統

「import できない」というエラーの正体です。

この章の要点
  • ファイルを分割して読み込む方式が2系統ある
  • Node が独自に作った旧方式(CJS)と、JavaScript の正式な標準(ESM)
  • 混ぜると動かない。ネットのコードが動かない原因の上位
  • 新しく書くなら ESM で統一すればよい。判断に迷う余地はない

なぜ2つもあるのか

JavaScript はもともとブラウザの中で小さな仕掛けを動かすための言語で、ファイルを分割する仕組みを持っていませんでした。1つのファイルに全部書く前提だったからです。

第2部で見たとおり、2009年に Node が JavaScript をブラウザの外へ持ち出しました。サーバー用のプログラムは大きくなるので、分割の仕組みがどうしても必要になります。そこで Node が独自に作ったのが CommonJS です。

その後、JavaScript 言語の側が公式の仕組み(ES Modules)を定めました。後から標準ができてしまったため、両方が残っています。

第1部で見た「歴史的な経緯がそのまま設計に残る」の一例です。技術的な優劣の問題ではなく、順番の問題です。

その2系統を並べると、こうなります。

CommonJS(CJS)ES Modules(ESM)
読み込みconst fs = require('fs')import fs from 'fs'
公開module.exports = ...export default ...
出自Node が独自に作った旧方式JavaScript 言語の正式な標準
拡張子.cjs.mjs

.js というありふれた拡張子がどちらとして扱われるかは、package.json の記述で決まります。

{ "type": "module" // これがあると .js は ESM 扱い } // 無ければ CommonJS 扱い

第3部で作った検証用スクリプトを check.mjs という名前にしたのは、この設定に依存せず確実に ESM として動かすためです。

よく出るエラー

Cannot use import statement outside a module ─ ESM の記法を CJS として読み込もうとしている。.mjs にするか "type": "module" を足す。

require() of ES Module ... not supported ─ 逆方向。ESM のパッケージを古い記法で読もうとしている。import に書き換える。

近年の Node は両者の相互運用を改善しており、以前ほど頻繁には詰まりません。ただしネット上の記事は両方の時代のものが混在しているため、コピーしたコードが動かない原因の上位であり続けています。

新規に書くなら ESM で統一するのが素直です。Next.js も ESM 前提で設計されています。

$ npm outdated

依存を管理する ─ semver とロック

バージョン表記の記号には、はっきりした意味があります。

この章の要点
  • バージョン番号の3つの数字には、それぞれ決まった意味がある
  • ^~ は「どこまで勝手に新しくなってよいか」の指定
  • package.json は範囲しか書いていない。1点を記録しているのが lock ファイル
  • 「昨日まで動いていたのに」の大半は、ここが原因
たとえ話

「最新版の様式でお願いします」

職員に「最新版の様式で作って」と指示したとします。あなたが指示した日と、職員が実際に着手した日の間に様式が改訂されていたら、指示は同じでも出てくる書類は違います

package.json^ はこの「最新版で」という指示にあたります。範囲を示しているだけで、一点を指定していません。

package-lock.json は「令和8年4月1日改訂版」と版を書き留めたメモです。これがあれば、誰がいつ作っても同じ書類が出てきます。

まずバージョン番号の読み方です。semver(セマンティック・バージョニング)という取り決めがあります。「意味を持たせた版番号の付け方」という程度の意味です。

位置名称上がるとき
1.2.3メジャー互換性が壊れる変更。要注意
1.2.3マイナー機能追加。既存の使い方は壊れない
1.2.3パッチ不具合修正のみ

package.json に付く記号は、どこまでの自動更新を許すかの指定です。

表記意味許容範囲
^1.2.3マイナーまで許す(既定)1.2.3 以上 2.0.0 未満
~1.2.3パッチのみ許す1.2.3 以上 1.3.0 未満
1.2.3完全固定1.2.3 のみ

ここで package-lock.json の意味が定まります。package.json範囲しか書いていません。同じ package.json でも、npm install した時期によって入るバージョンが変わります。

実際に起きる経路

package.json"next": "^15.0.0" と書いてあるとします。「15.x.x の範囲なら新しいものでよい」という意味です。

先月あなたが npm install したときは 15.2.1 が入りました。今月、別の職員が同じリポジトリで npm install すると、その間に公開された 15.3.0 が入ります。

同じ package.json なのに、手元の中身が違う。15.3.0 に不具合があれば、片方の環境だけ動きません。「私の環境では動くのですが」という状況の典型的な発生源です。

lock ファイルは、実際に入った1点を記録したものです。これがあるから、来月も、別のパソコンでも、Vercel のビルド環境でも、同じものが入ります。

第3部の CASE 3 の再訪

rm -rf node_modules package-lock.json という手順が「再現性を捨てる操作」だと書いたのは、このためです。lock を消すと、範囲の中の別のバージョンが入りうる。「昨日まで動いていたのに」の原因になります。

本番やビルド環境では npm ci を使ってください。lock ファイルどおりに厳密に入れ、範囲の解釈をやり直しません。

依存の連鎖と、そのリスク

第2部で「node_modules が巨大になるのは部品が部品を呼ぶから」と書きました。これを推移的依存と呼びます。自分が選んだのは3個でも、実際に取り込まれるのは数百個です。

ここには実際のリスクがあります。直接選んでいないパッケージが、自分の製品の中で動いているということだからです。過去には、広く使われていた小さなパッケージが悪意ある管理者に渡り、それを使う全プロジェクトに影響が及んだ事例が複数あります。

$ npm audit # 既知の脆弱性を確認 $ npm audit fix # 自動で直せるものを直す $ npm ls パッケージ名 # なぜそれが入っているのか、経路を表示

最後の npm ls が有用です。「このパッケージはどこから来たのか」を辿れます。顧客データを扱うシステムでは、依存を1つ増やす判断に、この確認を挟む価値があります。

$ next build

Next.js のレンダリング4方式

Next.js を使ううえで、最も中心的な設計判断です。

この章の要点
  • 問いは1つだけ。「HTML をいつ作るか」
  • 早く作るほど表示が速く、遅く作るほどデータが新しい。両立しない
  • SSG / ISR / SSR / CSR は、その軸の上の4つの点にすぎない
  • 選択がそのまま Vercel の費用に跳ね返る
たとえ話

弁当屋の4つのやり方

昼食をどう出すか、と考えると、そのまま4方式に対応します。

方式弁当屋でいうと結果
SSG朝のうちに全部作って並べておく出すのは一瞬。中身は朝のまま
ISR並べておくが、2時間ごとに作り直す速いうえ、そこそこ新しい
SSR注文を受けてから作るできたては保証。待たせる
CSR材料と手順書を渡して、客に作らせる店は楽。客の待ち時間が長い

作り置きは出すのが速い。ただし朝の情報のままです。注文後に作れば最新ですが、待たせます。速さと新しさは交換関係にあり、どちらも取ることはできません。

CSR が「客が自分で作る」なのは、ブラウザに材料(JavaScript)とデータを送って、組み立て作業自体をブラウザにやらせるからです。だから最初の待ち時間が長くなります。

問いは一つです。HTML を、いつ作るか。

ビルド時 リクエスト時 ブラウザ到着後 SSG 静的生成 HTML を生成 ISR 段階的再生成 生成 → 一定時間ごとに作り直す SSR サーバー描画 毎回その場で生成 CSR ブラウザ描画 ブラウザが組み立て
左へ行くほど表示が速く、右へ行くほどデータが新しい。ISR がその中間に位置します。
方式表示速度データの鮮度向く用途
SSG最速ビルド時点で固定会社案内、ブログ、料金表
ISR速い指定した間隔で更新お知らせ一覧、記事一覧
SSRやや遅い常に最新ログイン後の画面、検索結果
CSR初回が遅い常に最新管理画面、ダッシュボード

判断の仕方

2つの問いで、ほぼ決まります。

そのデータは、何秒古くても許されるか。会社案内なら1日古くても構いません。給与計算の結果なら1秒でも古いと困ります。

誰が見ても同じ内容か。全員に同じものを見せるなら、1回作って使い回せます。人によって違うなら、毎回作るしかありません。

古くてよい全員同じ選ぶべき方式
はいはいSSG
数分ならはいISR
いいえいいえSSR

顧問先ごとに違う情報を出す画面は、原理的に SSR です。事前に作り置きできないためです。逆に、事務所の紹介ページを SSR にするのは、毎回同じものを作り直しているだけの無駄になります。

ここが「Vercel の料金が思ったより高い」という事態の主な原因でもあります。SSG で済むページを SSR にしていると、アクセスのたびにサーバーが動きます。

$ grep -rn "use client" src/

サーバーとクライアントの境界

App Router の中心的な考え方であり、最も誤解されやすい部分です。

この章の要点
  • 既定ではすべてサーバー側で動く。コードはブラウザに送られない
  • "use client" と書いたものだけが、ブラウザにも送られる
  • これは「ブラウザ専用」という意味ではない。両方で動く指定
  • 取り違えると接続情報が外部から読める状態になる。実害が出る箇所
たとえ話

事務所の奥と、受付カウンター

奥の執務スペースがサーバーコンポーネントです。顧客からは見えません。だから金庫も開けられますし、他社の資料を机に広げていても問題ありません。

受付カウンターがクライアントコンポーネントです。顧客と直接やりとりする場所で、ボタンを押す、文字を入力するといった応対はここでしか起きません。

そして決定的な点です。カウンターに置いた書類は、顧客が読めます。だから機密書類をカウンターに置いてはいけない。"use client" のファイルに接続情報を書くとブラウザから読めてしまう、というのは、これとまったく同じことです。

奥からカウンターへ書類を出すことはできます(これが props)。逆に、カウンターから奥の金庫を直接開けることはできません。

Next.js の App Router では、すべてのコンポーネントが既定でサーバー側で動きます。ブラウザには送られません。ブラウザでも動かしたい場合に限り、ファイルの先頭に "use client" と書きます。

サーバーコンポーネント DB に直接アクセスできる 秘密鍵を安全に使える ブラウザに JS を送らない useState は使えない props クライアントコンポーネント useState が使える onClick が使える ブラウザ API が使える JS がブラウザへ送られる 境界を越えられるのは、そのまま文字にできる値だけ
矢印は一方通行です。サーバー側からクライアント側へ値を渡せますが、逆は直接できません。

なぜこの区別があるのか

サーバー側で動くコンポーネントは、そのコードがブラウザに一切送られません。ここから3つの利点が出ます。

データベースに直接触れます。API を経由する必要がありません。接続情報や API キーが漏れません。ブラウザに届かないからです。ブラウザに送る JavaScript が減ります。結果として表示が速くなります。

最も多い誤解

"use client" は「ブラウザだけで動く」という意味ではありません。初回の HTML はサーバー側でも作られ、その後ブラウザでも動き始めます。両方で動くのが正しい理解です。

なぜ両方なのか。もしブラウザだけで組み立てると、JavaScript を読み終わるまで画面は真っ白になります。それを避けるため、サーバー側で一度組み立てた HTML を先に送り、あとから JavaScript が届いて動き出す。この二段構えになっています。

名前が誤解のもとです。"use client" は「クライアント専用」ではなく、正確には「クライアントにも送る」という指定だと読んでください。

だから "use client" を書いたファイルに接続情報を書くと、ブラウザに送られて誰でも読めます。ここは実害が出る箇所です。

境界の引き方

実務上の原則は単純です。クライアント側は、必要な最小の葉先だけにする。

ボタンやフォームなど、実際に操作を受け付ける部分だけを "use client" にし、それを囲むページ全体はサーバー側に置く。逆にページ全体をクライアントにすると、サーバーコンポーネントの利点をすべて失います。

// app/staff/page.tsx ─ サーバー側(既定) export default async function Page() { const 一覧 = await db.select(); // DB に直接アクセス return <従業員テーブル rows={一覧} />; // 値だけを渡す } // app/staff/絞り込みボタン.tsx ─ ブラウザでも動く 'use client'; export function 絞り込みボタン() { const [開いている, 設定] = useState(false); // 状態を持てる ... }

渡せるのは、そのまま文字として表現できる値だけです。関数はそのまま渡せません。この制約が、境界の設計を強制します。

$ tree src/app

App Router のファイル規約

ファイル名そのものが機能を持ちます。

この章の要点
  • 決まった名前でファイルを置くと、それだけで機能する
  • 呼び出すコードは書かない。向こうが探しに来る
  • フォルダ名の記号([ ] ( ) _)にも意味がある
  • 暗記より、一覧を手元に置いて引くのが実用的

第1部の原則1「すべてはファイルである」が、最も直接的に現れている部分です。決まった名前でファイルを置くと、Next.js がそれを見つけて呼びに来ます。

なぜ名前で決まる方式なのか

「このURLではこの処理を呼ぶ」と設定ファイルに一覧を書く方式もあります。実際、多くのフレームワークがそうしています。

Next.js がそうしないのは、設定と実体がずれることを防ぐためです。設定ファイル方式では、ファイルを移動したのに設定を直し忘れる、という事故が起きます。二重管理だからです。

名前で決まる方式なら、ファイルを移動すれば URL も一緒に動きます。ずれようがありません。

ファイル名役割
page.tsxそのURLで表示される本体。これがあって初めてURLになる
layout.tsx配下のページを包む枠。画面遷移しても再描画されない
loading.tsxデータ待ちの間に表示される。自動で差し込まれる
error.tsx配下でエラーが起きたときの受け皿
not-found.tsx該当なしのときの表示
route.ts画面ではなく API として応答する

フォルダ名にも規約があります。

書き方意味
[id]可変部分。値として受け取れる/staff/1001
[...slug]階層をまとめて受け取る/docs/a/b/c
(group)整理用。URL には出ない(admin)/staff/staff
_folderルーティング対象から外す部品置き場など
src/app/ ├── layout.tsx 全ページ共通の枠 ├── page.tsx → / ├── staff/ │ ├── page.tsx → /staff │ ├── loading.tsx → 読み込み中の表示 │ └── [id]/ │ └── page.tsx → /staff/1001 └── api/ └── check/ └── route.ts → /api/check(API)

loading.tsx を置くだけで読み込み中の表示が入るのは、規約が機能を持つということの分かりやすい例です。呼び出すコードを書く必要はありません。第2部で触れたフレームワークの性質 ─ こちらが呼ぶのではなく、向こうが呼びに来る ─ がここに出ています。

API も同じ場所にある

route.ts を置けば、そこが API になります。フロント側とバックエンドが同じプロジェクトに同居する、という Next.js の性格がここに現れています。

第3部で作った Python スクリプトのような処理も、route.ts に移せばブラウザから呼べるようになります。「スクリプトから画面へ」の移行が、この1ファイルから始まります。

$ vercel deploy

Vercel との結びつき

なぜ Vercel だと素直に動くのか。逆に、離れると何が起きるのか。

この章の要点
  • Next.js の機能の一部は、実行する場所の協力がないと成立しない
  • Vercel はそれを標準で用意している。同じ会社が両方を設計しているため
  • 離れることは可能。ただしその分を自分で用意することになる
  • 第5章の選択が、そのまま毎月の費用に効いてくる

Next.js を作っている会社が Vercel である、という事実の実務上の意味を見ておきます。

前章までに出てきた機能のいくつかは、実行基盤の協力がないと成立しません

機能基盤側に必要なもの
ISR(一定時間ごとの再生成)生成済み HTML を保管し、期限を管理する仕組み
画像の最適化変換処理と、変換結果のキャッシュ
SSRリクエストごとにサーバー処理を走らせる環境
ストリーミング表示途中まで送りながら残りを作る通信方式

Vercel はこれらを標準で用意しています。だから git push だけで動きます。フレームワークと基盤を同じ会社が設計しているのだから、当然そうなります。

第5章の選択が、費用になる

ここで前の章とつながります。方式によって、基盤にかかる負荷がまったく違うからです。

方式アクセス1回あたりに起きること費用の増え方
SSGできあがった HTML を配るだけほとんど増えない
ISR基本は配るだけ。期限が来たときだけ作り直すゆるやかに増える
SSR毎回サーバーが動いて組み立てるアクセス数に比例して増える

SSG で済むページを SSR にしていると、誰かが開くたびにサーバーが同じものを作り直します。「Vercel の請求が思ったより高い」の主因はここです。

裏返せば、事務所の紹介ページのように内容が変わらないものを SSG に寄せるだけで、費用は下がります。設計判断が直接お金に変わる、数少ない分かりやすい例です。

自前で動かす場合

不可能ではありません。ただし、上の表の各項目を自分で用意することになります。ISR のキャッシュをどこに置くか、画像変換をどう処理するか、といった設定が必要です。

Next.js には出力を1つにまとめる設定があり、それを使って自前のサーバーや Docker で動かせます。とはいえ手間はゼロではない、というのが正直なところです。

これも設計判断

第2部第6章に書いた「依存を増やすということは、他人の責任範囲を増やすこと」が、ここでも当てはまります。

Vercel に寄せれば運用の手間が消えますが、その分だけ自分でコントロールできない部分が増えます。第3部で見た Gemini CLI の一件と、構造は同じです。

判断材料は用途で分かれます。事務所のサイトや社内ツールなら、寄せたほうが合理的です。顧問先のデータを長期に預かるシステムなら、移せる形にしておく価値が出てきます。

4部を通して

第4部で見たことは、結局こういう形にまとまります。

問い答え
Node が同時接続に強い理由1本のスレッドで、待ち時間を他の仕事で埋めているから
Node が重い計算に弱い理由同じ理由。1本しかないスレッドが塞がるから
await の意味止まるのではなく、順番を譲る
lock ファイルが要る理由package.json は範囲しか書いていないから
レンダリング方式の選び方データが何秒古くて許されるか、全員同じ内容か
"use client" の意味ブラウザ専用ではなく、両方で動くという指定

どれも、暗記する種類のものではありません。理由がわかっていれば、その場で導けるものばかりです。そして理由がわかっていると、AI が出したコードの誤りに気づけます。第2部の冒頭に書いた「出てきたものを疑う力」は、こういう形で実装されます。