前回の記事はこちら:
はじめに
前回の記事では、ダッシュボードUI(保管食材/即食食材の分類表示、個数管理、非食品判定、レシート風デザイン)を作った話を紹介しました。
今回は、食費と食品ロス額を可視化する家計簿機能と、レシート単位での履歴管理・削除機能を作った話をまとめます。機能実装というより、「使いながら気づいた欲しい機能」をその場でどんどん足していった回でした。
この記事はこんな方におすすめ
- 前回記事を読んでくれた方
- 個人開発で家計簿・集計系の機能を作りたい方
- AIとの対話で機能追加を繰り返すスタイルに興味がある方
- GitHub Codespacesの無料枠が気になっている方
実際に作ったもの
今回追加したのは、次の5つです。
- 家計簿ページ:今月の食費合計、食品ロス額(廃棄した金額)、カテゴリ別の内訳を表示。前月・翌月の切り替えにも対応
- 手動入力ページ:自販機や飲み会の会費など、レシートのない出費を日付指定で記録できる機能
- 履歴ページ:レシート単位(撮影1回分)で合計金額と明細をまとめて表示し、間違って登録したレシートを丸ごと削除できる機能
- ナビの横スクロール対応:ページ数が6つまで増えたので、折り返さず横スワイプできる形に変更
- 使い方ページ:各ページの説明をまとめた案内ページ
いずれも「作ってから使ってみて、足りないと感じた部分をその場で拡張する」という進め方で積み上げました。
設計判断:レジ袋を家計簿に含めるかどうか、実際にやってみて
前回の記事で、「レジ袋のような非食品は、一覧には出さず家計簿には含める」という設計にした話を書きました。今回家計簿機能を実装してみて、実際にその設計が効いた場面がありました。
typeがnon_foodのデータもpriceはそのまま持っているので、家計簿の合計金額を出すクエリでは何も特別なことをせず、単純に全件のpriceを合計するだけで、レジ袋代も含めた正しい食費合計が出せました。逆に冷蔵庫一覧側はtype === "stored"とtype === "immediate"だけを拾っているので、non_foodは何もしなくても自動的に一覧から外れます。表示側でフィルタする設計にしておいたことで、後から生えてきた家計簿機能に手直しはいりませんでした。
詰まりポイント:レシート単位の管理ができていなかった
機能を追加しながらテストを繰り返しているうちに、「このレシート、テストで登録しすぎてぐちゃぐちゃだから消したい」という場面が出てきました。ところが、当初のテーブル設計にはレシート単位でグループ化する仕組みがなく、商品1件ずつしか管理できませんでした。
そこでproductsテーブルにreceipt_idという列を追加し、1回の撮影で読み込んだ商品には同じIDを振るようにしました。
const receiptId = crypto.randomUUID();
const rows = items.map((item) => ({
// ...他の項目
receipt_id: receiptId,
}));
これで、履歴ページではreceipt_idごとにグループ化して「このレシートは合計いくらで、中身は何か」を表示し、削除もレシート単位でまとめてできるようになりました。
ただし、この列を追加する前に登録していたテストデータにはreceipt_idが入っていないため、結局それらは削除して仕切り直すことになりました。後から「まとめて扱いたいデータ」が出てくることを見越して、グループ化のためのIDは最初から振っておいた方がよかったという反省点です。
GitHub Codespacesの無料枠、実際どれくらい使う?
開発機がChromebookのみという構成上、GitHub Codespacesに開発環境そのものを頼っているのですが、無料枠(月60時間相当)がどれくらいのペースで減っているのか気になったので、実際に確認してみました。
github.com/settings/billing → 「Usage」から、Codespacesの消費量がグラフで確認できます。今回、3日間触った時点で消費額は$1.22(無料枠内で相殺され請求は$0)。標準の2コア環境がだいたい$0.18/時間なので、逆算すると3日間で7時間弱という計算になります。体感的にはもっと長時間触っていた気がしていたので、この数字を見て「このペースなら無料枠でも十分やっていけそうだ」と一安心しました。
ポイントは、タブを閉じただけでは止まらず、一時停止中も無料枠を消費し続けるということです。使い終わったら明示的に「Stop」しておくのが無難です。長丁場になりそうな個人開発では、この使用状況の確認画面を一度見ておくと安心材料になります。
正直、この7時間には実装だけでなく記事執筆や試行錯誤に費やした時間もかなり含まれています。最初のうちはUIの場所を探すだけで時間がかかっていましたが、後半になるにつれて操作に慣れて、機能追加のペースは明らかに右肩上がりになっていきました。数字だけ見ると「意外と少ない」と感じるかもしれませんが、内訳としては「最初は手探り、慣れてから加速」というごく自然な曲線だったと思います。
このBilling画面を見るまで、正直料金のことはほとんど意識していませんでした。改めて数字として眺めてみると、レシート撮影からAI解析、DB保存、家計簿、履歴管理まで一通り動くアプリが、制限付きとはいえ今のところ完全無料で作れているというのは、素直にすごいことだと思います。
まとめ
今回は新しい技術要素を学ぶというより、「今あるデータをどう組み合わせて見せるか」という回でした。家計簿機能がほぼ集計だけで作れたのは、前回の設計判断(データは残す、表示だけ制御する)が効いていた結果だと思います。逆にreceipt_idのように、後から必要になって慌てて追加した設計もあり、「将来グループ化したくなりそうなデータには、最初からIDを振っておく」という教訓を得ました。
次はPhase 3の残り、期限が近い食材を使ったレシピ提案機能に進む予定です。その前に、Gemini APIの無料枠を使い潰した話(とその後リトライ処理まで作った話)を番外編としてまとめます。
続きはこちら(公開後にリンクを追記します)