動機
お小遣い管理と聞くと、普通は「いくら使ったか」「いくら残っているか」を管理するものだと思います。でも我が家の場合、お小遣いは毎月定額で、増えも減りもしません。管理すべきは金額ではなく、「今月、渡したか・受け取ったか」だけでした。
しかも、現金手渡しではなく、コーヒー豆の空き缶に入れておくという運用のため、気づいたら缶にお金は入っているけど、それが今月分なのか、いつの分なのか分からない、という状態になります。
僕自身ほとんどお金を使わないタイプで、 昼ごはんも抜き、自販機も使わず、数年に一度PCを組み替えるときだけ大量出費する、という生活をしています。その結果、
僕「今月もらったっけ?」
妻「渡したと思うけど…?」
という、どちらも悪くないのにモヤっとする会話が定期的に発生するようになりました。
そこで、「金額を管理するアプリ」ではなく、 「もらった/渡したを確定させるだけのアプリ」を自作することにしました。
画面イメージ
まず、完成した画面はこんな感じです。
月ごとにカードが並び、
それぞれに「嫁」「自分」用のスイッチがあります。
スイッチをONにすると、
- 一度ONにしたらOFFに戻せない
- 表情が変わる
- 感謝を伝えるため嫁側は紙吹雪が出る
という、かなり情緒寄りのUIになっています。
技術構成
今回のアプリは、以下の構成で作っています。
- フロントエンド:React
- UI:MUI (Material UI)
- 認証:Firebase Authentication(メールログイン)
- データベース:Cloud Firestore
- ホスティング:自前サーバー
かなりシンプルな構成で、状態管理もほぼすべてFirestoreに寄せています。
ワンウェイUIの実装
このアプリの一番の特徴は、一度ONにしたらOFFに戻せない点です。
実装自体はとてもシンプルで、SwitchのonChangeで「ONの時だけ処理する」ようにしています。
<Switch
checked={a}
color="secondary"
onChange={(_, nextChecked) =>
onToggleMaaboo(key, nextChecked)
}
/>
ここで呼ばれている onToggleMaaboo は、次のような実装になっています。
const onToggleMaaboo = useCallback(
(key, nextChecked) => {
if (!canEditMaaboo) return;
if (!nextChecked) return;
if (maabooData?.[key] === true) return;
setFlagTrue("maaboo", key);
},
[canEditMaaboo, maabooData, setFlagTrue]
);
ポイントは、
- nextChecked が true のときだけ処理する
- すでに true の場合は何もしない
- ログインユーザー本人のみ操作可能
という条件をすべてここで制御している点です。
これにより、
-
一度ONにしたら戻せない
-
誤操作で履歴が壊れない
-
「渡した/もらった」が確定する
というワンウェイなUIを実現しています。
UIで意識したこと(“儀式”としての操作)
このアプリの操作は月に一度しか発生しません。 だからこそ、押した瞬間に「ちゃんと確定した」という感覚が残るようにしました。
押しても地味に状態が変わるだけだと、次の月には存在を忘れがちです。 そこで、スイッチをONにしたタイミングで
- 表情が変わる
- 紙吹雪が出る
といった “気持ちいい反応” を入れています。
特に紙吹雪は「達成」ではなく「感謝」を伝えるためです。
お小遣いを渡す側(妻)が、渡す行為をいやいやの作業にしないように、受け取った側が「ありがとう」を表現できる仕組みにしました。
月1回の小さなやり取りを、なるべく気持ちよく終わらせることがこのUIの狙いです。
まとめ
このアプリは、技術的にすごいことをしたわけではありません。でも、「お小遣い管理」というテーマをそのまま受け取るのではなく、
「本当に困っているのは何か?」
を考えた結果、管理すべきは金額ではなく「渡した/受け取ったという事実」だと気づきました。
月に一度の小さなやり取りが、お互いに気持ちよく終わるようにすること。それがこのアプリの一番の目的です。
個人開発では、技術よりも「課題設定の方が難しくて面白い」ことを改めて実感しました。

