記事一覧に戻る

Tsuzumi — 行動心理学に基づくモバイル習慣トラッカーの設計と実装

React NativeExpoFirebaseCloud FunctionsTypeScriptモバイルアプリ行動心理学

解決すべき課題

習慣化を続けるのは難しいという課題に対して、既存アプリを調査した結果、市場には2つの極があることがわかりました。

カテゴリ代表例問題点
ソロ型トラッカーLoop, Streaks一人で続けるため孤独、モチベーション維持が困難
ソーシャル型Habitica, Habitifyチャット・コメント前提で重い、SNS疲れと相性が悪い

この中間に「誰かの存在は感じたいが、コミュニケーションコストは払いたくない」というニーズがあると判断しました。

設計思想: 「繋がりすぎない繋がり」

本アプリのコアコンセプトは テキスト・音声コミュニケーションを完全に排除した軽量ソーシャル です。以下の設計原則をすべての機能判断の基準としています。

原則内容具体的な判断例
軽さ最優先1タップで記録完了量や質の入力は求めない。「やったかどうか」だけ
匿名性プロフィール不要「筋トレの仲間」としてのみ表示。ユーザーIDやカード名は非公開
アプリ主導マッチングもエール提案もアプリがコントロールユーザーは「送る/送らない」を選ぶだけ
ポジティブ体験失敗を責めない、再開を称えるストリーク途切れを強調しない設計

行動心理学の知見の適用

設計には以下の理論的背景を取り入れています。

理論アプリへの適用
Fogg Behavior Model1タップ記録で「実行しやすさ」を最大化
Habit Loop(習慣ループ)エールを「報酬」として習慣ループを強化
自己決定理論外発的報酬より「成長感」「共走感」を優先
セルフ・コンパッション失敗を責めず再開を称える設計(復活バッジ)

特に「復活の一歩」バッジは、中断後に3日間再開した場合に獲得できるバッジです。連続記録(ストリーク)を主役にすると、1日途切れただけで離脱するリスクがあります。このバッジは「戻ってきていい」というメッセージを設計に組み込むためのものです。

技術選定

技術選定理由
React Native + ExpoiOS/Android両対応が必須。Expo Routerのファイルベースルーティングで開発効率を確保
Firebase (Firestore)リアルタイム同期がエール通知に必要。匿名認証でログインハードルを排除
Cloud Functionsエール生成・バッチ通知などサーバーサイドロジックをサーバーレスで実行
TypeScript型安全性による保守性確保。特にFirestoreのデータモデル定義で効果を発揮

匿名認証の採用 は、コンセプトとの整合性から決定しました。「すぐに始められる」体験を最優先し、メールアドレス登録のハードルを排除しています。将来的にアカウント昇格(メール/SNS連携)が可能な設計にしてあります。

段階的開発: 10フェーズの意思決定

MVP思考で段階的に機能を追加しました。各フェーズの区切りは「常に動くプロダクトを維持する」ことを基準としています。

Phase内容判断の根拠
1-6基本記録・Firebase連携・ストリーク・統計コア機能を先に完成させ、動作する状態を確保
7AIエール機能(Cloud Functions)コールドスタート問題への対策。ユーザーが少ない段階でも「応援される体験」を提供
8人間エール機能AIエール基盤の上にユーザー間リアクションを追加
9UX改善(3ボタンダイアログ、長押しショートカット)実機テストで得たフィードバックを反映
9.5カード管理強化・ニックネーム機能重複防止(Levenshtein距離による類似度検出)、作成上限(50枚)など運用安全性を確保
10Aお気に入り機能エール送信先の固定化ニーズへの対応
10Bセキュリティ修正10Aで一時的に緩和したFirestoreセキュリティルールを修正

セキュリティ対応の具体例

Phase 10Aでお気に入り機能を実装した際、開発速度を優先してFirestoreのセキュリティルールを一時的に緩和しました。機能の動作確認後、Phase 10Bとしてセキュリティ監査を実施し、以下の脆弱性を修正しています。

  • リアクション機能における送信者の偽装可能性を修正
  • データの公開範囲設定が意図通りに機能していない箇所を修正
  • 不要な権限の削除

セキュリティルールの緩和と修正を別フェーズとして明示的に管理し、緩和状態のまま放置しない運用を徹底しています。

テスト導入によるバグ発見

Phase 8で自動テスト基盤(Jest + Firestoreモック)を導入しました。テスト作成の過程で、logServiceのストリーク計算が0を返すバグと、statsServiceの週開始日計算の問題を発見しています。手動テストでは見逃していた問題が、テストコードの作成時に顕在化した実例です。

CI/CD戦略についても、当初の計画を見直しました。テスト整備Phase 1→2→3の順序で進める予定でしたが、「CI環境(GitHub Actions)が最も価値が高い」と判断し、Phase 3を最優先に変更しています。最小限のスモークテストでCIパイプラインを先に構築し、テストカバレッジは段階的に拡充する方針です。

AIエール機能の設計

最も設計に時間をかけた機能です。Cloud Functionsで4つのパターンを実装しました。

パターントリガー設計意図
記録直後習慣記録の5-45分後即時フィードバック。意図的な遅延で「監視感」を排除
継続途切れ未記録の翌日(週2回上限)優しい復帰誘導。脅迫的にならないよう頻度制限
長期離脱7日/21日/35日後(最大3回/カード)「待っている」メッセージ。段階的に間隔を空ける
ランダム6時間ごと不意の嬉しさ。直近1週間に記録があるユーザーのみ対象

68種類の文言を用意し、同じ文言の繰り返しによる「ロボット感」を排除しています。また、お休みモード(デフォルト23:00-07:00)を実装し、ユーザーの生活リズムを尊重しています。

AIエールと人間エールを区別しない 設計も意図的な判断です。どちらも「ハビット仲間」として表示し、「応援されている」感覚を分断しないようにしています。

実装上の技術的課題と対応

SafeAreaView の無限ループ

SafeAreaViewのリファクタリング時に無限ループが発生しました。原因はuseFavoritesフックの不要な状態更新と、buildSections関数の状態更新パターンにありました。

対応として、ヘルパー関数をuseCallbackでメモ化し、状態更新を関数型アップデートに変更しました。レイアウトコンポーネントのアーキテクチャも見直し、_layout.tsxSafeAreaProviderのみを提供する構成に整理しています。

Android / iOS の時刻ピッカー分岐

設定画面の時刻選択UIで、Androidのネイティブダイアログとの互換性問題が発生しました。iOSではモーダル表示、Androidではネイティブダイアログと、プラットフォームごとに実装を分離することで対応しています。

Lottieアニメーションの統合

起動画面と記録成功時のアニメーションにLottieを採用しました。起動画面ではタップで即座にスキップできる仕組みを実装し、アニメーションを待たされるストレスを排除しています。記録成功時のアニメーションも3.5秒で自動消去されますが、タップで即座に閉じることも可能です。

リリース準備

Google Play向けのリリース準備として、以下を実施しました。

  • EAS Buildによる本番ビルド(AABファイル生成)
  • アプリ名のリブランディング(「Tsuzumi」に統一)
  • データ削除ページの作成(Google Play要件)
  • データセーフティ情報の整備

不採用とした機能と理由

設計段階で検討し、意図的に不採用としたものがあります。

機能不採用理由
ランキング・レベル・経験値競争・比較要素はSNS疲れと相性が悪い。軽さを損なう
テキストチャットコミュニケーションコストが上がり、コアコンセプトに反する
プロフィール公開匿名性の維持が設計の根幹
ストリーク途切れの強調表示完璧主義を助長し離脱リスクを高める

「何を作るか」と同時に「何を作らないか」を明確にすることで、コンセプトの一貫性を維持しています。

技術スタック

技術用途
React Native + Expo (Router)クロスプラットフォームモバイルアプリ
TypeScript型安全なコードベース
Firebase Firestoreリアルタイムデータ同期
Firebase Anonymous Authログインハードル排除
Cloud Functionsエール生成・バッチ通知
Lottieアニメーション
EAS Build本番ビルド(AAB)