コンテンツにスキップ

AI Session Notes - 2026-02-10

Next.js Hydration Error: タイムゾーン依存の値

学んだこと

  • toLocaleTimeString() はサーバー(UTC)とクライアント(ユーザーのタイムゾーン)で異なる結果を返すため、SSR時にHydration mismatchが発生する
  • 対策: タイムゾーン依存の値は state を空文字で初期化し、useEffect 内でのみセットする(クライアントサイドオンリー)
  • これは Date.now(), Math.random(), ロケール依存のフォーマットなど、サーバーとクライアントで結果が異なるすべての値に当てはまる

詳細

// NG: SSRとクライアントで値が異なる
const [time, setTime] = useState(new Date().toLocaleTimeString("ja-JP"));

// OK: 初期値を空にして useEffect でセット
const [time, setTime] = useState("");
useEffect(() => {
  setTime(new Date().toLocaleTimeString("ja-JP", { hour: "2-digit", minute: "2-digit", second: "2-digit" }));
}, []);

Docker + Next.js ホットリロードでのファイル破損

学んだこと

  • Docker volume マウント環境で Next.js (Turbopack) のホットリロード中にファイルが途中で切れる(truncate される)ことがある
  • 症状: Expected ',', got '<eof>'Unterminated string constant などのパースエラー
  • ファイル自体が壊れていない場合でも Turbopack のキャッシュが壊れた状態を参照していることがある
  • Turbopack が CJS module can't be async というエラーを出す場合も、ホットリロードの不整合が原因の場合がある

対策

  1. ファイルを丸ごと Write で書き直す(Edit だと不十分な場合がある)
  2. それでもダメなら dev サーバーを再起動(docker compose restart app
  3. 頻繁に起きる場合は、ファイルの保存頻度を下げるか polling を調整する

クライアントサイドフィルタリングパターン

学んだこと

  • サーバーで全データを取得し、クライアントコンポーネントに渡してフィルタリングするパターンは、即座にUI切り替えができて体験が良い
  • フィルタ条件のデータ(例: お気に入りID一覧)もサーバーで並列取得して初期propsとして渡す
  • 自動リフレッシュ時にフィルタ条件も一緒に更新すれば、常に最新状態を保てる

詳細

// Server Component: 全データ + フィルタ条件を並列取得
const [items, filterIds] = await Promise.all([fetchItems(), fetchFilterIds()]);
return <List items={items} filterIds={filterIds} />;

// Client Component: フィルタをローカル state で管理
const [filter, setFilter] = useState<"all" | "favorites">("all");
const filtered = filter === "all" ? items : items.filter(i => filterIds.has(i.id));

外部APIのFavorite/お気に入り機能の汎用パターン

学んだこと

  • 多くの外部APIでは「お気に入り」は汎用的なFavoriteエンティティとして実装されている(type フィールドで種類を区別)
  • Favoriteエンティティは対象の実IDを持つだけで、対象の詳細情報は別途取得が必要
  • 効率的な実装: お気に入りIDのセットを先に取得 → メインデータとクライアントサイドでクロスリファレンス

詳細

GET /favorites?type=friend → [{ favoriteId: "usr_xxx", type: "friend" }]

favoriteId がユーザーIDなので、既に取得済みのフレンド一覧と Set で突き合わせるのが最も効率的。個別にユーザー情報を取得する必要がない。

.dockerignore によるビルド高速化

学んだこと

  • Docker ビルド時、.dockerignore がないとプロジェクト全体(node_modules 含む)がビルドコンテキストとして転送される
  • node_modules, .next, .git, data などを .dockerignore に追加するだけで転送量が劇的に減る(例: 309MB → 2KB)
  • Dockerfile 内で pnpm install しているなら、ホスト側の node_modules は不要
  • docker compose up -d --build--build フラグは毎回イメージビルドを強制する。ソースがボリュームマウントされている開発環境では、依存関係の変更時だけ --build すれば十分

Next.js + Docker 開発環境でのセッション管理アーキテクチャ

学んだこと

  • Server Actions パターンでは認証情報を2層に分離できる:
  • ブラウザ ↔ アプリ: 暗号化 Cookie(iron-session 等)でログイン状態のみ管理。秘密情報は含めない
  • アプリ ↔ 外部 API: トークンはサーバー上のファイルやメモリにのみ保存。ブラウザに一切漏らさない
  • この分離により、外部 API のトークンがブラウザに露出するリスクを排除できる
  • 注意点: HTTP(非 HTTPS)環境ではブラウザ側の Cookie が平文で送られるため、LAN 外への公開には HTTPS が必須
  • シングルトンで API クライアントを管理する場合、複数端末から同一セッションを共有する形になる(一方のログアウトが他方にも影響)

CLAUDE.md によるAIエージェントへのルール定義

学んだこと

  • AIコーディングツール向けのプロジェクトルールは、具体的なファイル名をハードコードするよりも「起点となるファイルを参照して判断する」形にすると拡張性が高い
  • 例: 「docs/features.md を更新すること」ではなく「docs/index.md を確認して影響を受けるドキュメントがあれば更新すること」
  • 目次ファイル(index.md)を正しく管理しておけば、ドキュメントが増えてもルール自体を変更する必要がない

メタ情報

  • ツール: Claude Code
  • 関連技術: Next.js, React, TypeScript, Docker, Turbopack, Server Actions, iron-session, Cookie, セキュリティ