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というエラーを出す場合も、ホットリロードの不整合が原因の場合がある
対策
- ファイルを丸ごと Write で書き直す(Edit だと不十分な場合がある)
- それでもダメなら dev サーバーを再起動(
docker compose restart app) - 頻繁に起きる場合は、ファイルの保存頻度を下げるか 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, セキュリティ