テスト前提で設計したWebアプリのハンズオン - 読書管理アプリ その5

はじめに Part4 でカスタム例外クラスと残りのエンドポイントを実装し、APIが完成した。 Part5では src/app/page.tsx に簡易UIを実装する。バックエンドのロジックやテストには一切触れない。 フロントエンドからAPIを叩いて動くものを作るだけだ。 実装方針 page.tsx をClient Componentにする。 Server Componentでfetchする方法もあるが、ボタン操作のたびにstateを更新して再描画する必要がある。 今回のUIは「ボタンを押す → APIを叩く → 一覧を再取得して画面を更新する」という流れが中心なので、 useState + useEffect で管理するClient Componentのほうがシンプルだ。 "use client"; ファイル先頭にこの1行を追加する。 実装するUI 本の追加フォーム(id / title / isbn) 本棚(Unread / Reading / Completed のグループ表示) 各本へのアクションボタン Unread → 「読み始める」ボタン Reading → 評価入力(1〜5)+「読了にする」ボタン 全ステータス → 削除ボタン 型定義 APIレスポンスの型を定義する。 type BookStatus = "Unread" | "Reading" | "Completed"; type Book = { id: string; title: string; isbn: string; status: BookStatus; rating: number | null; }; バックエンドの ReadingStatus は as const で定義した文字列リテラルなので、そのまま使える。 データ取得 async function fetchBooks() { try { const res = await fetch("/api/books"); if (!res.ok) throw new Error("取得失敗"); const data: Book[] = await res.json(); setBooks(data); } catch (e) { setError(e instanceof Error ? e.message : "不明なエラー"); } finally { setLoading(false); } } useEffect(() => { fetchBooks(); }, []); fetchBooks は追加・ステータス変更・削除の後にも呼ぶ。 サーバーの状態を正として再取得するシンプルな方針だ。 楽観的更新(Optimistic Update)は今回やらない。 ...

March 5, 2026 · 2 min