業務効率化

「話しかけていいですか」を可視化するアプリを企画してみた話。安全設計で何度も振り出しに戻った記録

2026年8月23日

最近、個人開発のネタとして「同じ場にいる人同士をゆるくつなぐアプリ」を企画していました。最初はふわっとした思いつきだったんですが、要件を詰めていくうちに何度も設計を作り直すことになり、結果的に「安全設計をどう考えるか」についてかなり学びがあったので、備忘録としてまとめておきます。

最初のアイデアと、方向転換の連続

そもそもの発端は「異性と話すきっかけを作るWebアプリ」という、かなりざっくりしたお題でした。マッチング系にするか、会話ネタを生成するツールにするか、いくつか案を出しては引っ込め、を繰り返していました。

途中で「ホラーオチを入れたい」というアイデアも出たんですが、これは企画の軸がブレるだけだったので早々に白紙に戻しました。凝った演出を先に考えるより、まず誰のどんな課題を解決するのかを固めるのが先だったな、というのは今振り返っての反省です。

方向性が二転三転する中で、一番大きな転換点は「ナンパ成功率を上げるアプリにしたい」という案が出たタイミングでした。声をかけられる側が同意も選択もしていない設計は、対等なコミュニケーションのきっかけ作りとは別物だと気づき、ここは明確に方向を変えました。同意ベースの設計に切り替えたことで、企画としての筋が一本通った感覚があります。

オフラインマッチングという着地点

方向転換した先で行き着いたのが「その場にいる人同士をマッチングする」というコンセプトです。カフェやバーで、アプリ上で「話しかけられてもOK」なユーザー同士が近くにいると分かる、というものですね。

ここで最初に考えたのがBLE(Bluetooth Low Energy)を使った近接検出でした。スマホ同士が直接通信して、周囲にいる人のステータスをリスト表示する仕組みです。技術的には実現可能で、react-native-ble-plxのようなライブラリを使えば組めそうだったんですが、検討を進めるうちにいくつも壁にぶつかりました。

BLEを選んで見えてきた壁

まず大きかったのが権限まわりです。AndroidではBLEスキャンに位置情報権限が必要になるケースがあり、「Bluetoothの機能を使いたいだけなのに、なぜGPSの許可を求められるのか」というユーザーの違和感につながりやすい構造になっていました。Android 12以降であればneverForLocationフラグで回避できる場合もあるんですが、古い端末には対応しきれません。

さらに厄介だったのがiOSのバックグラウンド制限です。アプリを閉じた瞬間、BLEのアドバタイズ(自分の存在を発信する動作)がほぼ止まってしまいます。つまり「ポケットにスマホを入れっぱなしで自動的にマッチする」という体験は、iOSでは技術的に実現できません。アプリを開いたままにしてもらう前提で設計するしかなく、そうなると当然バッテリー消費も気になってきます。画面を暗転させて省電力を狙うアイデアも出しましたが、結局「起動維持は使うための必須コスト」と割り切ることになりました。

方向転換その2。BLEを手放してWeb完結に

ここで大きな発想の転換があったのが「客にインストールさせるハードルが高すぎるのでは」という気づきでした。ダーツバーのような業態を想定していたんですが、その場限りの利用のためにアプリをインストールしてもらうのは、正直かなり高いハードルです。

そこで舵を切ったのが、店内共通のQRコードを読むだけでアクセスできる、インストール不要のWebアプリという方式でした。BLEを手放した瞬間、実は色々な問題が同時に解決していきます。iOSのバックグラウンド制限も、Androidの位置情報権限の違和感も、そもそもBLEを使わなければ関係なくなるわけです。

ただし、当然トレードオフもありました。BLEなら「物理的に近くにいる」ことがある程度担保されていたのに対して、QRコード方式だとURLさえ知っていれば誰でもアクセスできてしまいます。ここで新しい安全設計の問題が浮上しました。

「店の外から見えてしまう問題」との格闘

QRコードのURLは、写真に撮ってSNSで共有したり、友人に転送したりすることが技術的には可能です。つまり店に行っていない人でも、URLさえ知っていれば「今その店に何人いて、誰が話しかけてOKか」を外部から覗き見できてしまう。これはそのままストーキングや待ち伏せのリスクに直結します。

対策として色々な案を検討しました。IPアドレスでの店内判定、営業時間帯でのアクセス制限、人数だけ公開して個別情報は隠す案など。ただ、検討していくとIPアドレスでの位置判定はスマホの回線事情もあって実質機能しないことが分かったり、人数だけ公開する案は結局覗き見のリスクを部分的にしか防げなかったりと、帯に短し襷に長しという状態が続きました。

最終的に落ち着いたのが、「自分もステータスを登録した人だけが、他の人の一覧を見られる」という相互主義の設計です。加えて、ステータス登録の瞬間に一度だけGPSで店舗との近接を照合し、離れた場所からの不正登録を防ぐ仕組みを組み合わせました。位置情報は照合の瞬間だけ使い、保存はしない設計にすることで、常時追跡のリスクも避けています。

「まだいますボタン」の連打問題

地味に面白かったのが、ステータスの自動失効の設計です。最初は「一覧を開くたびに自動延長」というシンプルな案を考えていたんですが、これだと店を出た後もアプリを開き続けるだけで「まだ店にいる」ことにできてしまい、出待ちの温床になりかねません。

かといって「まだいますボタン」を手動で押させる方式にしても、それを連打され続けたら同じ問題が形を変えて残ります。結局、固定時間での自動失効に加えて、初回登録からの絶対上限(今回は4時間)を設け、それを超えたら強制的にQRの再読み込みからやり直させる、という二段構えに落ち着きました。物理的にその場にいないと突破できない壁を、どこかに必ず一つ挟んでおく、というのが今回一番学んだ設計の勘所だったかもしれません。

思わぬ方向への飛び火。センシティブな使い方には線引きが必要

企画を広げていく過程で、「この仕組みは職場やクラブ活動にも応用できるのでは」というアイデアが出ました。これ自体は自然な発想の広がりだったんですが、その延長で「退職を考えている社員のステータスを、本人に見せずに経営者側にだけ通知する」というアイデアが出た時は、明確に線を引く必要がありました。

「今話しかけていいか」を本人が公開する情報と、「退職の意向」を本人の知らないところで上司に伝える情報は、似ているようで全く性質が違います。前者は本人の意思表示ですが、後者は力関係が非対称な中で、本人の同意なく機微な情報が流れる設計になってしまいます。便利そうに見えるアイデアほど、それが本当に「本人のための機能」なのか、それとも「本人を管理する側のための機能」なのかを、都度立ち止まって考える必要があるんだなと痛感しました。

技術構成を決める中での気づき

技術スタックはNext.js+TypeScript、DBはPostgreSQL(Supabase)、ホスティングはVercelという、個人開発でよく見る組み合わせに落ち着きました。ここで意識したのは、バージョンを最初から厳密に固定しないことです。相性の悪いパッケージ同士でエラーが出るのはよくある話なので、package.jsonはできるだけ^でレンジ指定にして、導入コストを抑える方針にしました。

もう一つ学びだったのが、店舗ページのカスタマイズ機能を検討していた時の話です。「店舗独自のHTMLを埋め込めるようにしたい」というアイデアが出たんですが、これは実装するとXSS(クロスサイトスクリプティング)の温床になりかねません。店舗アカウントが乗っ取られた場合、そのページを見る一般利用者全員が攻撃対象になってしまいます。同じ「見た目をカスタマイズしたい」というニーズは、背景色やロゴ画像など構造化された項目に絞ることで、安全に実現できることが分かりました。自由度と安全性はトレードオフになりがちですが、要件を分解すると意外と両立できるポイントが見つかるものですね。

まとめ

今回の企画は、最初のアイデアからかなり遠いところに着地しました。振り返ってみると、方向転換のたびに「便利さ」と「安全性」がぶつかる場面が何度もあって、そのたびにどちらかを一方的に切り捨てるのではなく、要件を分解して両立点を探る、という作業の繰り返しだったように思います。

BLEを手放してWeb完結にした判断、GPS一回照合という落としどころ、絶対上限を設けた自動失効の仕組み、構造化されたカスタマイズ項目でXSSを避けた設計。どれも「便利にしたい」という要求に対して、一歩立ち止まって「それを悪用するとしたらどうなるか」を考えることで見えてきたものでした。

個人開発だとどうしても機能の面白さや実装のしやすさに気持ちが向きがちですが、特に人と人をつなげる系のサービスは、悪用された時の被害が個人に直接及びやすい領域です。今回のプロセスを通じて、企画の初期段階からセキュリティ・プライバシーの視点を組み込んでおくことの大切さを改めて実感しました。実装フェーズはこれからなので、また進捗があれば追ってご報告しようと思います。

Related Posts