本文へスキップ
← 作ったもの一覧

KOTSULY

個人開発 / 開発中

毎日の積み上げを Vlog のワンシーンのように記録し、仲間と応援し合う自己研鑽SNS。iOS / Android を先に、Web 版も同じコードから準備しています。

何を解決するものか

資格の勉強や筋トレ、早起きのような「毎日の積み上げ」は、ひとりだと続きません。かといって、ふつうの SNS に投稿すると、いいねの数やフォロワーと比べてしまい、かえって疲れます。

KOTSULY(コツリー)は、比べずに、ゆるく見守り合えることを目指した自己研鑽SNSです。

  • 記録:取り組み(簿記2級・毎朝ラン など)を選んでひとこと書くと、Vlog のワンシーンのようなカードになります。写真がなくても、取り組みや時間帯に合わせた絵で仕上がります
  • 作業スペース:カフェ・ワークラウンジ・図書館のフロアから席を選んで作業します。話さなくても、近くでがんばる人の気配が伝わります
  • 作業ルーム(Study with me):誰かが開いたタイマーに途中から参加して、みんなで同じポモドーロを回します
  • モーニング・ナイトルーティン:朝と夜にやることを並べて、タイマーに合わせてひとつずつ進めます。終わるまでタイムラインは見せません
  • ふりかえり:毎週日曜の夜に、その週の積み上げを先週の自分と比べて届けます

いまはベータテストの準備中です。iOS / Android のアプリを先に出し、Web 版も同じコードから準備しています。

担当範囲

企画・要件定義・UI設計・実装(アプリ・Web 版・公式サイト)・DB設計・インフラ・運用のすべて。利用規約とプライバシーポリシーの下書きも自分で作っています。

開発は Claude Code を中心に進めていて、セッションを「モバイルアプリ開発部」「デザイン部」「経営企画室」のような部署に分けて回しています。わたしの役目は、決めることと、実機で確かめることです。くわしくは「Claude Code に『部署』を作って、ひとりで個人開発を回している話」に書きました。

技術的に難しかった点と、どう解いたか

1. 「比べない」を、画面ではなく DB で守る

このサービスの方針は「数で比べさせない」ことです。応援の数は記録した本人にしか見せず、ランキングも置きません。

ただ、画面で隠すだけだと、API を直接たたけば他人の応援の数が取れてしまいます。そこで Supabase の RLS(行ごとのアクセス制御)で、応援を読めるのは「記録の本人」と「応援した本人」だけにしました。アプリに不具合があっても、DB が返さないので漏れません。

2. 公開範囲とブロックを、すべてのテーブルで一貫させる

記録には「全体 / フォロワー / 自分だけ」の公開範囲があり、さらにブロックが絡みます。これを記録・コメント・応援それぞれの決まりに書き散らすと、どこか一箇所で必ず抜けます。

判定は can_see_post() という関数1つにまとめ、コメントも応援も「元の記録が見えるか」をこの関数に聞く形にしました。宣伝目的の新しいアカウント対策(リンクを貼れるのは記録が7日以上ある人だけ)のような決まりも、アプリと同じものを DB に置いています。

3. 「誰がどの席にいるか」を、DB に書かずに配る

作業スペースは、いつも誰かがいる前提の機能です。素直に作ると、座る・席を移る・休憩するたびに書き込みが走ります。

在席は Supabase Realtime の Presence、スタンプとひとことは Broadcast にして、どちらも DB には書きません。Presence はつなぎが切れると自動で消えるので、退室の処理も、残った行の片付けもいりません。

  • 「座りました」「休憩に入りました」は送らず、Presence の前と今の差から各端末で作る
  • 混んだら2号店・3号店が自動で開く。号店ごとにチャンネルは増やさず、在席の中身の番号で分ける
  • みんなで囲む席のポモドーロは、時刻を配らず、各端末が「毎時0分・30分」から計算する

4. 作業中のタイマーを、スマホと Web のあいだで同期する

iPhone で始めた作業を、パソコンの Web 版で続きから見て、そこで終えて記録できるようにしています(Web 版は準備中です)。

  • 残り時間は送らない:送るのは始めた時刻だけで、経過やポモドーロの残りは各端末が計算する
  • 勝ち負けは DB が決める:「作業中は1人1つまで」のユニーク索引と、「まだ終わっていない行だけ更新する」条件で、2台で同時に始めた・終えたときも二重の記録にならない
  • 放置された作業は、端末とサーバーの両方で終える:3時間ごとに「まだ作業中ですか?」と聞き、どの端末も開いていないときは pg_cron が終える

5. アプリと Web 版を、同じコードから作る

最初は Vite + React の Web の試作品で、「数タップで記録できるか」「応援が届くと続けたくなるか」を確かめました。そのあと、型・データ・計算・状態管理を同じ関数名のまま Expo に移し、画面だけを React Native で書き直しています。

Web 版も、このモバイルアプリのコードから書き出す形にしていて、iOS / Android / Web を1つのコードで回せるようにしています。サーバーにつないでいないときは端末の中だけで動く「デモモード」になるので、サーバーがなくても全画面を触れます。

6. 重いファイルは R2、リアルタイムは Supabase

個人で運用する以上、1,000人規模でも月数千円に収めることを前提にしました。

  • 記録の写真は Supabase Storage の非公開の置き場所に置き、期限つきの URL で見せる。アップロードの前に端末で縮め、位置情報などを消す
  • 作業BGM(自作の曲)は Cloudflare R2 の非公開の置き場所に置き、Edge Function が期限つきの URL を出す
  • 認証メールは自分のドメインから Resend で送る

学んだこと / 次にやること

SNS は「何を作るか」以上に「何をさせないか」の設計が品質を決める、というのがいちばんの学びでした。比べさせない・さらさせない・荒らさせないといった決まりは、画面ではなく DB に置いて初めて守れます。

リアルタイムの機能も同じで、在席は保存しない、出来事は送らない、時刻は配らない、と送らないものを決めるほど、壊れる場所も費用も減っていきました。

次は、身近な人に使ってもらうベータテストを始め、そのあと App Store と Google Play で公開する予定です。開発のようすは X(@kaede3eng) でときどき書いています。

お仕事のご相談

「まだ要件が固まっていない」「実現できるか相談したい」の段階で構いません。技術的に可能かどうかだけでもお答えします。

お問い合わせフォームへ

メールでも受け付けています → dkae1107@gmail.com