一言で
感情記録とAI分析を提供するZero GravityのSpring Boot REST APIです。 フリーティアクラウド上にインフラからデプロイまで自ら構築し、コスト制約の中でAI機能を設計しました。
- 5つのドメイン、15個のエンドポイント — 認証、感情記録、統計、AI分析を一つのAPIとして設計しました。
- OCI + Terraform 6モジュールでインフラを自ら構築し、Build-first戦略でZero-Downtimeデプロイを実現しました。
- Time BucketサンプリングでGemini API送信量を97%削減し、AI機能をコスト上限内で運用しました。
チームプロジェクトからプロダクションまで。
チームプロジェクトとして始めたSpring Bootバックエンドをプロダクションサービスに転換しました。 基本的なCRUD APIのみ存在していたプロジェクトに認証、インフラ、デプロイパイプラインを構築し、AI分析機能を追加しました。
- レイヤードアーキテクチャをドメイン別構造に再構成しました。
- NextAuth OAuth → JWT認証体系を実装しました。
- OCI上にインフラを自ら構築し、Zero-Downtimeデプロイを実現しました。
- Gemini APIベースの感情分析機能を追加しました。
API Endpoints
Infrastructure
全部送ると一ヶ月も持ちません。
Gemini APIで感情記録を分析する機能を実装しようとしました。 1年分の記録を全て送るとリクエストあたりのInputトークンが~55Kに達しました。プロジェクト予算では賄えませんでした。
データを減らすと分析品質が下がり、機能自体を諦めるにはコア機能でした。
既存のデータから答えを見つけました。
全てを送れないなら、各期間を代表する記録だけを選んで送ればよかったのです。 問題は「代表」をどの基準で選ぶかでした。
チャートAPIの期間別平均レベルとバケット別最頻理由をサンプリング基準として使用しました。 「この月の平均レベルに最も近く、最頻理由を含む」記録1件を各バケットから選別しました。
バケット一つ、代表記録一つ。
期間を均等な時間単位(バケット)に分割し、各バケットから最も代表的な記録1件を選択しました。 上位12件をそのまま抽出すると特定の月に集中する可能性があるため、バケット単位で均等に分割しました。
感情レベル60% + 理由マッチング40%の加重スコアで各バケットの代表記録を選別しました。 Daily記録は一日全体を振り返って書いた記録のため、1.5倍の重みを適用しました。
同点の場合は日記が長い順、理由が多い順、最新順でタイブレークしました。
例: Year分析の1月バケット (平均感情レベル: 4.5、最頻理由: "Work")
AI分析結果は24時間キャッシュし、感情記録が変更されると該当期間のキャッシュを無効化しました。
送信量97%削減、リクエストあたり$0.002
制約が戦略を生みました。 機能を諦める代わりに、既存のデータを新しい文脈で再活用する発想で問題を解決しました。