1. はじめに
きっかけは、会社の人間ドックでした。メタボ対策の一環として、3か月間の健康管理プログラムに参加することになり、体重や食事を記録するためのアプリも提供されました。
せっかく記録する習慣がつくなら、3か月で終わらせず、その後も継続していきたい。そう思ったのですが、どうせ続けるなら、自分で使いやすいものを作ってみることにしました。
最初は体重と食事をExcelで記録していました。ただ、数か月分たまってくると欲が出てきます。毎日の体重は上下にブレるので、本当に減っているのか分かりにくいし、スマホからサッと入力もしたい。どうせなら、データは自分のAWSに置きたい。
そんなわけで、ふと思い立ってダッシュボードを作り始めたのですが、振り返ると2か月半で3回作り直していました……。
| 期間 | 形 | 進め方 |
|---|---|---|
| 2026年4月中旬まで | Excel | 手入力 |
| 4月下旬(3日間) | Flask + ReactをRenderでホスト | AIエージェントでVibe Coding |
| 4月下旬(1日) | AWSサーバーレスに全面移行 | 同上 |
| 4月下旬〜7月上旬(約2か月半) | AWS上で運用しながら改善 | AI-DLCに切り替え |
ここでいう「3日」「1日」は、その間ずっと画面に張り付いていたという意味ではありません。平日は仕事の合間、休日は用事の合間にAIエージェントへ指示を出しておき、手が空いたら結果を見て次を頼む。そんな片手間の作業です。
最初からAWSできれいに作ったわけではなく、遠回りもハマりも、作りすぎて削ったものもあります。本記事は、その試行錯誤の備忘録です。
💡 スクリーンショットの体重・体脂肪率・食事の数値は、すべて乱数で作ったダミーデータです(私の実際の記録ではありません)。使ったCSVはこちらからダウンロードでき、アプリの「CSVで置き換える」でそのまま取り込めます。
2. 現在の構成
先に現在のダッシュボードツールの形です。コードはGitHubで公開しています。
本記事の内容は、執筆時点のコード(タグ
blog-2026-09)に基づいています。リポジトリは今後も更新していくので、最新版はmainを参照してください。
体重は日々の値と7日移動平均を重ねて表示し、ジムやHIITをした日も点で示しています。
直近30日の体重に直線を当てはめて「週あたり何kg増減しているか」を出すグラフや、目安を外れた日を赤くする栄養素のグラフもあります。
スマホでは縦1列に並びます。

データのマスターは手元のCSVで、Webアプリはそれを見るためのダッシュボード、という位置付けです(最初からそう決めていたわけではなく、公開前に決め直しました。7章で書きます)。
構成は次のとおりです。
| レイヤー | 技術 |
|---|---|
| フロントエンド | React + TypeScript + Vite + Recharts(Amplify Hosting) |
| 認証 | Cognito(メールアドレス + パスワード、セルフサインアップ無効) |
| バックエンド | API Gateway + Lambda(Python 3.12 / ARM64) |
| データベース | DynamoDB(オンデマンド) |
| IaC | AWS CDK(TypeScript)。AmplifyアプリもCDKで管理 |
3. Vibe Codingでプロト作成、Renderで動かす(3日)
最初は「Excelを読んでグラフのHTMLを吐き出すPythonスクリプト」1本でした。翌日にはFlaskでWebアプリにしてRender(GitHubのリポジトリをつなぐだけでWebアプリを公開できるPaaS。無料プランあり)にデプロイし、3日目には画面をReactに作り替えています。使ってみると次にほしいものが見えてくる、の繰り返しで、業務なら腰が重くなる全面リファクタリングもAIエージェントなら数回のやり取りで終わりました。
ただ、回り道もしています。
- データの持ち方が1日でExcel → JSON → CSVと3回変わった(CSVに揃えてようやくシンプルに)
- 並行して作った2つのブランチをマージしたら、APIが二重定義になって削除機能が消えた。AIはブランチの中では正しいコードを書いてくれますが、ブランチをまたいだ整合性までは見てくれません
- 最後の1行を消すと集計が
IndexErrorで落ちる、という「空っぽ」のケースは誰も考えていなかった(記事を書くときに気づきましたw)
そしてRender(Freeプラン)には、再デプロイでデータが消えるという問題がありました。認証もBasic認証1本で、健康データの置き場としては正直厳しい。ということで、4日目にAWSへの移行を決めました。
4. AWSへの移植(1日で完了?!)
Render版のコードを新しいリポジトリにコピーして、構成をまるごと置き換えました(公開しているリポジトリの最初のコミットがRender版の最終形です)。
| 役割 | Render版 | AWS版 | 理由 |
|---|---|---|---|
| API | Flask | API Gateway + Lambda | 使うのは1日数回。常時起動は不要 |
| データ | data.csv | DynamoDB | 消えない、使った分だけの課金 |
| 認証 | Basic認証 | Cognito | パスワードを自分で管理しない |
| インフラ | 画面で手動設定 | CDK | 壊しても作り直せるように |
移行のコミットは1本ですが、同じ日にfixが20本以上続きます。エラーを貼って直してもらい、別のことをしながら待つ、の繰り返しでした。主なハマりは2つでした。
Lambdaが起動しない
デプロイ直後、LambdaがRuntime.ImportModuleErrorで起動しませんでした。手元のMac(Apple Silicon)でpandas / numpyをARM用にビルドしていたのに、Lambdaはx86_64で作っていたのが原因です。LambdaをARM64に揃えたら、今度はIntel MacでビルドするとExec format error。。。最終的に、バンドル時にプラットフォームを明示して決着しました。
const bundling: cdk.BundlingOptions = {
image: lambda.Runtime.PYTHON_3_12.bundlingImage,
platform: 'linux/arm64', // ARM64 用の wheel を取得する
command: ['bash', '-c', 'pip install -r requirements.txt -t /asset-output && cp -r . /asset-output'],
};
CDKの循環依存
フロントエンド(Amplify)にはAPIのURLを、Cognitoにはログイン後に戻る先としてAmplifyのURLを渡す必要があり、お互いを参照して一周していました。2段階デプロイなどで回避していましたが、5月上旬に「普段はカスタムドメインしか使わないのだから、AmplifyのURLをコールバックに入れる必要はない」と気づいて、依存そのものをなくしました。AIは回避策はきちんと出してくれますが、要件のほうを変える判断は人間の仕事だなと感じた場面です。
ほかにも、JWTを付け忘れたfetchで401、CSVエクスポートの文字化け、CloudFormationの出力に日本語を書いて???????など、細かいものがいろいろありました。この日の最後のコミットはCognitoのセルフサインアップ無効化です(セルフサインアップが有効なままだと、誰でもアカウントを作れてしまうことに気づきました^^;)。
5. AI-DLCに切り替えて育てる
AWSに移ると、「思いつきで変えて、壊れたらすぐ直す」がやりにくくなります。フロントエンドはpushで自動デプロイされますが、Lambdaとインフラはcdk deployしないと反映されません。そこで、前回の「Rust版コスト監視ツール」でも試していたAI-DLCを、今度は既存のプロジェクトに当てはめてみることにしました。
using ai-dlc, このプロジェクトを正確に分析した上で、AI-DLCに従い、今後の保守を踏まえ、ドキュメントを生成したい。
AIがコードを読み込んで、要件や設計をaidlc-docs/にまとめてくれます。使ってみて一番助かったのは、設計書よりもaudit.md(監査ログ)のほうでした。何を指示して、AIがどう判断したかが時系列で残ります。カスタムドメインを作り直したときは、ドメイン設定がブランチ作成より先に走って失敗したり、Route 53に古いCNAMEが残ってSSL設定が通らなかったりしたのですが、その経緯も全部このログに残っています。(後から振り返ることができる、例えば、本記事を書くときの一次情報になっております!)
監視は、作りすぎて削りました^^; AWS移植後にAPI Gateway・Lambda・AmplifyのアクセスログとCognitoの認証ログを取り始め、GW後にS3に長期保存する仕組みも入れたのですが、
- 「認証失敗」を数えるつもりのクエリが、実は「試行」を数えていた(トリガーがパスワード検証の前に動くため)。名前を
auth-attempt-statsに直しました - 全ロググループをS3に送っていたけど、見返すのは3つだけだった
と、実態に合わせて絞っています。個人のツールでここまで要るのか、と言われるとその通りなのですが、インターネットに公開している以上、誰がいつ入ろうとしたかは見えるようにしておきたかった、というのが背景です。
機能は、実際に使いながら少しずつ足していきました。体重だけでは変化が分かりにくかったので、体脂肪率のグラフを追加。食事も数字を並べるだけでは良し悪しが分かりにくいため、目安を外れた日は赤く表示するようにしました。タンパク質と食物繊維だけは、「摂りすぎ」ではなく「不足」が気になるので、目安を下回ったときに赤くします。
さらに、「体重が減ったのは運動したから?」と後から振り返れるように、ジムやHIITをした日も表示。曜日による傾向も見たくなって、曜日表示も追加しました。CSVを何度も出力しているうちにファイルを見分けにくくなったので、ファイル名には連番を付けるようにしました。
こうした機能の多くは、朝のコーヒーを飲みながら「これもあると便利だな」と思いついたものです。思いついたら、その場でエージェントに指示して修正し、実際に使ってみる。気になるところがあれば、また直す。そんなサイクルを朝の短い時間に繰り返していました。
「AIを使えば開発が速くなる」とはよく聞きますが、朝食を食べる前に思いついた機能が実装されていくのを見て、文字どおり「朝飯前」を体験した気がします。
※この頃、GitHubのIssueにコメントを書くだけでAIエージェントに直してもらえるようにもしたので、実装はあっという間です。
6. Vibe CodingとAI-DLCの使い分け
振り返ると、向き不向きがはっきりありました。
- Vibe Codingは、何を作るかがまだ曖昧なときに向いています。とにかく速い。ただ、異常系やブランチ間の整合性は抜けやすく、残るのはコードとコミット履歴だけです
- AI-DLCは、動いているものを壊さずに変えるときに向いています。1件ごとに要件確認が入るぶん遅いですが、判断の経緯が残ります
今回の私の使い方は、「最初はVibe Codingで速く作って、残すと決めたらAI-DLC」でした。とはいえ、切り替えたあとずっとAI-DLCだったわけでもなく、audit.mdの記録は途中で止まっています。今回の公開前の修正は、直す中身がはっきりした小さな修正だったので、AI-DLCは通さずプルリクエスト単位で進めるなど臨機応変な活用になっています。作業の大きさで使い分けるくらいが、個人開発にはちょうどいいと思います。
7. 公開前の見直し
ツール公開にあたりコードを見直しました。これもAIエージェント頼りで、合間を見ながら1日でプルリクエストを10本以上出して直すというものでした。
公開前の修正点(AIによるコードレビュー)
- 平均カロリーが実際より低く出ていた。欠損値を
fillna(0)で埋めていたので、食事を記録しなかった日が「0kcal」として平均に入っていました。入力されてない場合は無視するように修正済みです - DynamoDBの削除ポリシーが
DESTROYで、cdk destroyするとデータごと消える設定でした。RETAINにし、ポイントインタイムリカバリを有効にしました - GitHubのトークンがCloudFormationテンプレートに平文で残っていました。これは流石に頂けず、Secrets Managerに置き、テンプレートには参照だけが残るように修正しました。トークンも入れ替えています
- (個人開発ということもあり)テストは1本も実装してませんでした。AIエージェントにお願いし、集計ロジックにpytestを足してもらいました
その他修正点(画面レビュー)
ブラウザを操作できるAIエージェントに本番の画面を触ってもらい、画面の数字をCSVから計算し直した値と突き合わせてもらいました。計算は合っていたのですが、
| レビューで見つけたこと | 対応 |
|---|---|
| 「30日」に絞ると平均カロリーが0kcal(その期間は記録がなかった) | 「記録なし」と表示 |
| 食事を記録していない日の入力フォームに0が入っていて、体重だけ直して保存すると0kcalが記録される | 0は空欄として扱う |
| グラフごとに日付の書式や小数の桁がばらばら | そろえた |
| 増減ペースの最初の週だけ大きく振れる(点が少ないのに回帰していた) | 14日分そろうまで表示しない |
上の2つは、平均カロリーと同じfillna(0)が原因でした。「記録していない」と「0だった」を区別できなくなると、あちこちで別々のバグになります。
どちらがマスターかを決め直した
最後は設計の話です。記事のスクリーンショットはダミーデータで撮りたい。そこで「ダミーのCSVを読み込んで、公開したら実データのCSVに戻す」つもりでいたのですが、コードを見ると、CSVの取り込みは日付ごとに書き込むだけで、CSVにない日付は消さない作りでした。このままだと、実データがスクショに写り込むし、戻したあともダミーがゴミとして残ります。
原因は、CSVとDynamoDBのどちらが正なのかを決めないまま作っていたことです。そこで、CSVをマスター、Webアプリはそれを映すダッシュボードと決めて、取り込みを「置き換え」に変えました。CSVを読み込むと、DynamoDBの中身はそのCSVとまったく同じになります。
置き換えはCSVにないデータを消す操作なので、消すのは書き込みが終わってから、空のCSVは受け付けない、実行前にバックアップを勧める、の3つを入れています。
この記事のスクリーンショットも、実データをCSVで退避 → ダミーのCSVで置き換え → 撮影、という流れで撮りました。公開したら、実データのCSVで置き換えて戻します。データベースを直接触る作業は1つもなく、読み込むCSVを変えるだけです。
💡 インポート機能を作るときは、「追記」なのか「置き換え」なのか(どちらがマスターなのか)を最初に決めておくのがおすすめです。決めずに作ると、データの置き場が2つになって、エラーも出ないまま静かにずれていきます。
どれもAIのせいというより、動いたからヨシで先に進んだ自分の問題と認識しております。AI-DLCでも、聞かなかったことは誰も検証してくれません。逆に、画面を触って数字を突き合わせて、と頼めばAIはそこまで付き合ってくれました。
8. コスト
最後に、Cost Explorerの実績から、AWSに移行した4月下旬から9月下旬まで(約5か月)を集計しました(同じアカウントの別の検証環境の分は除いた推定です)。
| 月 | 金額 | 何があったか |
|---|---|---|
| 4月(下旬〜) | $0.71 | 移行初日。fixのたびにビルド |
| 5月 | $0.34 | カスタムドメインの作り直し、ログのS3保存 |
| 6〜9月 | 月$0.02〜0.05 | ほぼS3の保存料 |
| 合計 | 約$1.19 | 大半はAmplifyのビルド代 |
費用がかかるのは、ほぼデプロイした日だけでした。4章・5章の試行錯誤が、そのまま請求に出ていますw 使っているだけなら月4セントくらいです。Render(Freeプラン)は$0でしたが、データが消えない安心料としては十分安いと思います。
注意点
- 公開前の修正でGitHubのトークンをSecrets Managerに移したので、月$0.40が加わる想定です(結果、固定費は月$0.44位の見立て)
- 独自ドメインの費用(Route 53など)は別アカウントなので入っていません
9. まとめ
Excelの手入力から始まったダッシュボードは、Render版(3日)、AWS版(1日で移行)、AI-DLCでの運用(2か月半)、そして公開前の見直し、と少しずつ形を変えてきました。作り直すたびに、前の版に足りなかったものが見えてきた気がします。
AIまわりの技術は変化が速く、すべてを追いかけようとすると大変です。
ただ、今回のように「自分が実際に使うもの」を題材にすると、必要になった技術をその都度調べ、試し、失敗しながら覚えていけます。単に新しい機能を触ってみるよりも、実運用の中で困りごとが出て、その解決のためにAIやAWSを使うほうが、ずっと身につきやすいと感じました。
これからも、全部を追いかけようとするのではなく、自分で使いながら遊んで、必要になったものを一つずつ試していこうと思います。
Amazon Web Services、およびその他のAWS商標は、米国およびその他の諸国におけるAmazon.com, Inc.またはその関連会社の商標です。





