CodexとChatGPT SitesでリアルタイムWebアプリを作る方法|スマホでビンゴ開発記

※本記事は、公開中のWebサービス「スマホでビンゴ」を題材にした開発記です。個人を特定できる情報、実際の管理用リンク、参加コード、認証情報、ホスティングの識別子、ローカル環境のパスは掲載していません。

「ChatGPTのCodexに頼めば、本当に複数人が同時に使うWebアプリまで作れるのか」「見た目だけのモックではなく、データベースやリアルタイム同期を含むサービスを公開するには、何を頼めばよいのか」と気になっている方も多いのではないでしょうか。

今回は、司会者の端末で数字を抽選すると、複数の参加者のスマートフォンへ結果が届き、カードの穴開け、リーチ、ビンゴまで自動判定する完全無料の「スマホでビンゴ」を、CodexとChatGPT Sitesで作った手順を紹介します。

結論から言うと、完成度を分けたのは「AIへ一度頼んで終わり」にしなかったことです。最初に仕様書を固定し、状態遷移とデータ構造を決め、司会者・参加者の実ブラウザを同時に動かしながら不具合を直し、最後に公開範囲と本番ページを確認しました。

この記事でわかること

  • CodexとSitesへ最初に渡した情報
  • リアルタイム同期を成立させた設計
  • 見た目だけのモックで終わらせない指示の出し方
  • 実機テストで見つかった問題と修正内容
  • Sitesで保存版を作り、本番公開するまでの流れ
目次

作ったもの:司会者と参加者が同時につながるビンゴ

完成した「スマホでビンゴ」は、会員登録やアプリのインストールをせずに遊べる75球式のオンラインビンゴです。

  • 司会者がイベント名を入力して会場を作る
  • 参加者は読み取りコードまたは参加コードから入る
  • 表示名を入れると、一人ずつ異なる5×5カードが発行される
  • 司会者が抽選すると、参加者のカードへ結果が反映される
  • 当たった数字は自動で開き、リーチとビンゴも自動判定される
  • 通信が途切れても、同じ端末・ブラウザから最新状態へ戻れる
  • 1会場あたり最大200人を想定する

紹介用の静的ページではなく、司会者の操作が他の端末の状態を変えるWebアプリです。そのため、画面を作るだけでは足りず、権限、状態保存、同時操作、再接続、重複処理まで設計する必要がありました。

完全無料のスマホでビンゴのトップページ
公開中の「スマホでビンゴ」。ブラウザだけで会場作成と参加ができます

スマホでビンゴを実際に見る

CodexとChatGPT Sitesは何を担当したのか

OpenAIの公式案内によると、ChatGPT SitesではインタラクティブなWebサイトや軽量アプリを作成し、プレビュー、公開、共有まで進められます。Codexでは、ローカルのソースを読み、コードを編集し、テストし、Sitesへつながる公開作業まで同じ会話の中で進められます。

今回の役割分担は次のようになりました。

担当実際に行ったこと
人が決めたこと誰が、どの会場で、何に困っているか。無料で使えること。初めての人にもわかる日本語にすること。紙のカードらしい見た目にすること
Codex仕様の読み込み、実装計画、画面・サーバー・データベースの実装、テスト、複数ブラウザ検証、修正
Sites永続データ用ストレージを含むホスティング、保存版の管理、本番デプロイ、公開範囲、独自ドメイン接続

大切なのは、「ビンゴアプリを作って」だけで終わらせないことです。AIは実装を速く進められますが、サービスの優先順位や受入条件が曖昧なら、動いているように見えるだけの結果にもなります。

実際に採用した技術構成

仕様書の初期案をそのまま固定するのではなく、Sitesで運用しやすく、複数端末の同期と永続保存を両立できる構成へ調整しました。

領域採用したもの役割
画面Next.js、React、TypeScriptトップ、会場作成、司会者画面、参加画面、ビンゴカード
サーバーCloudflare Worker互換の実行環境権限確認、抽選、状態遷移、同期用の応答
データベースCloudflare D1、Drizzle(スキーマ/マイグレーション)会場、参加者、カード、抽選履歴、操作結果の保存
入力検証TypeScriptとサーバー側バリデーションイベント名、表示名、操作内容の検証
テストNode Test、Playwrightルール、同期、権限、同時操作、複数ブラウザの確認
公開ChatGPT Sitesストレージ接続、保存版、本番デプロイ、公開範囲、独自ドメイン

画面とAPIを同じプロジェクトに置きつつ、ゲーム結果の正しさはブラウザではなくサーバーへ集約しています。

手順1:仕様書を唯一の機能要件として固定する

最初に、サービス概要、利用者、画面、機能要件、非機能要件、受入条件、テスト計画までを一つの仕様書へまとめました。そしてCodexには、「この仕様書を唯一の機能要件として、本番運用可能なMVPを作る」と伝えました。

最初の依頼で特に効いたのは、次の3点です。

  1. 静的なモックではないと明記する:司会者端末と複数の参加者端末が同期することを必須にしました。
  2. 実装前の設計を求める:実装計画、ディレクトリ構成、データ構造、状態遷移を先に出させました。
  3. 受入条件で確認する:「できたと思う」ではなく、会場作成、参加、抽選、再接続、取消、権限などを具体的な条件で確認しました。

最初の指示を一般化したテンプレート

添付した仕様書を唯一の機能要件として、本番運用可能なMVPを実装してください。静的なモックではなく、管理者端末と複数の利用者端末がリアルタイムに同期するWebアプリとして完成させてください。

最初に実装計画、ディレクトリ構成、データベース設計、状態遷移を提示し、その後に実装してください。完了時は、要件と受入条件ごとに対応状況を報告してください。

別のサービスを作る場合は、「管理者」「参加者」「ビンゴ」の部分を、予約管理、投票、受付、在庫共有などに置き換えられます。

手順2:画面より先に状態遷移を決める

リアルタイムアプリで先に決めるべきなのは、ボタンの色ではなく「今、何が許される状態か」です。今回のゲームは、次の4状態に整理しました。

状態できることできないこと
参加受付中参加、表示名登録、受付終了抽選
受付終了受付再開、ゲーム開始新規参加、抽選
ゲーム中抽選、直前の抽選取消、ゲーム終了新規参加、受付再開
ゲーム終了結果の確認状態を変える操作

遷移は「参加受付中 ⇄ 受付終了 → ゲーム中 → ゲーム終了」です。ゲーム中から受付へ戻す経路や、終了後に抽選する経路は作りません。

この線引きをサーバー側で検証することで、古い画面が残っていた場合や、同じボタンが連続して押された場合にも、不正な状態へ進みにくくなります。

手順3:データベースでは「結果」より「正本」を保存する

今回は、Sitesで永続的な構造化データを扱うため、リレーショナルデータベースを使いました。Sitesの公式開発者ガイドでも、保存した記録、利用者の進捗、ゲームのスコアなどにはD1を使う構成が案内されています。

データは、概念として次の単位に分けました。

データ保存する内容
会場イベント名、参加状態、進行状態、状態の版番号、作成・開始・終了時刻
参加者表示名、自分専用のカード、リーチ・ビンゴ状態、最終接続時刻
抽選履歴何番目の抽選か、出た数字、有効か取消済みか
処理記録同じ操作が再送されたときに、二重実行を防ぐための記録
会場イベント参加、開始、抽選、取消、終了など、会場で起きた変更の記録
レート制限短時間に集中する操作を抑えるための集計
匿名分析表示名やカード内容を含めない、利用状況の改善用イベント

穴が開いたマスを一個ずつ保存しない

設計上の重要な判断は、各マスの「開いている・閉じている」を個別に正本として保存しなかったことです。カードの数字と有効な抽選履歴があれば、どのマスが開くかは毎回計算できます。

カード + 有効な抽選数字 → 開いたマス → リーチ・ビンゴ判定

この方式なら、抽選を取り消したときも全員のカードを同じルールで再計算できます。通信から復帰した端末も、最新の抽選履歴を受け取れば同じ状態を再構築できます。画面ごとに穴開き情報がずれる原因を減らせました。

手順4:リアルタイム同期は「通知」と「正しい状態取得」を分ける

開発で最も時間をかけたのが、司会者の抽選直後に参加者のカードが更新される部分です。

初期版ではServer-Sent Events(SSE)による通知も実装しましたが、本番で数字の反映が遅れる場面があり、長時間接続やキャッシュ境界も含めて同期経路を見直しました。方式の名前へ固執せず、現在は認証付きの長ポーリングによる変更通知と、版番号付きのREST同期を組み合わせています。

一定間隔で状態を取りに行く方法だけでも動きますが、抽選直後の数秒はゲームの体感を大きく左右します。そこで、次の構成にしました。

  1. 司会者が抽選すると、サーバーが抽選結果と全参加者の判定を保存する
  2. 会場全体の「状態の版番号」を一つ増やす
  3. 参加者側は変更通知を待ち、より新しい版があるとわかったら状態を取り直す
  4. 通知用の接続が不安定な場合は、短い間隔の同期へ切り替える
  5. 復帰時は版番号を比較し、取りこぼしがあれば完全な最新状態を取得する

通知そのものにカード全体を詰め込むのではなく、「新しい状態があります」と知らせた後、権限を確認して正しい状態を取得します。これにより、通知を一つ取りこぼしても、次回の同期で最新状態へ追いつけます。

直前の版から一つだけ進んだ通常抽選では小さな差分を返し、参加者のブラウザ側でカードを再計算する最適化も入れています。版が飛んだ場合、再接続時、不整合が疑われる場合は、データベースを基準にした完全な状態を取り直します。

「リアルタイム」を感覚ではなくテスト可能にする

「リアルタイムでお願いします」だけでは、1秒なのか5秒なのか、通信断から戻れるのかが不明です。そこで、次のように具体化しました。

  • 抽選結果が参加者画面に現れた時刻を記録する
  • 該当マスが開いた時刻を別に記録する
  • 複数の独立したブラウザで同じ抽選を観測する
  • 通信通知が使えない場合の代替同期も確認する
  • 古い版の画面が、新しい版を上書きしないことを確認する

不具合を直すときは、「反応しない」ではなく、「司会者の抽選後、参加者3台で最新数字と穴開きがいつ反映されたかを計測し、通知・状態取得・画面更新のどこで止まったか特定する」と依頼すると、原因へ近づきやすくなります。

手順5:二重抽選と同時操作をサーバーで防ぐ

イベント会場では、ボタンを連打したり、通信が遅くて同じ操作が再送されたりします。フロント画面のボタンを一時的に無効にするだけでは不十分です。

そこで、変更を伴う操作には冪等キーを付け、処理済みなら保存した同じ結果を返す設計にしました。さらに、会場単位の短時間リース、期待する状態バージョンの比較、抽選数字と抽選順のデータベース一意制約を重ねています。

これにより、同じ抽選要求が再送されても、別の数字が二重に出るのではなく、最初の結果を安全に返せます。仕組みは公開できますが、実際の認証値や個別セッションの識別情報は公開しません。この考え方は予約登録や注文作成にも応用できます。

手順6:司会者と参加者で見える情報を分ける

司会者には参加者一覧やゲーム操作が必要ですが、参加者には自分のカードと公開された抽選結果だけで十分です。そこで、画面と取得データを役割ごとに分けました。

  • 司会者用:受付、参加者一覧、抽選、取消、終了、リーチ・ビンゴ者
  • 参加者用:自分の表示名、自分のカード、最新数字、抽選履歴、自分の判定
  • 公開用:イベント名と参加受付状態など、参加前に必要な最小情報

管理用リンクは推測されにくい匿名の識別情報で保護し、保存時はそのままの値を持たない形にしました。非公開のゲーム画面や管理画面は検索対象にせず、キャッシュにも残りにくい応答にしています。

ただし、「絶対安全」という意味ではありません。管理用リンクを知る人が操作できるため、参加者やSNSへ共有せず、司会者だけで保管する運用が必要です。

ゲーム情報には保持期限を設ける

開始されなかった会場は作成から48時間後、終了した会場は終了から24時間後に関連データごと削除します。ゲーム中の会場は進行途中で自動削除しません。表示名やカード内容を含めない匿名の利用状況イベントは最長90日で削除します。

保持期限は、実装だけでなく公開中のプライバシーポリシーにも記載し、利用者が確認できるようにしました。

手順7:機能完成後に、紙らしい体験へ作り直す

最初に機能が動いても、それだけでは楽しいビンゴになりません。実際に遊ぶと、デジタルカードは「穴が開いたかどうか」が紙よりわかりにくい問題がありました。

そこで、参加者カードを白い紙のような質感へ変更し、開いたマスは次の要素を組み合わせました。

  • 色をグレーへ変える
  • 中央に打ち抜いたような形を出す
  • 開いた瞬間だけ光と動きを加える
  • 数字は薄く残し、何番だったか確認できるようにする
  • 色だけでなく、紙穴の形、陰影、アニメーションでも状態を伝える

リーチ時には、対象となる5マスを蛍光色の青い線で結び、どの列があと一つなのかを視覚化しました。複数のリーチが重なる場合も、線とマスの強調がわかるように調整しています。

右上の音声ボタンも、アイコンだけでは意味が伝わらなかったため、現在の状態と次の動作がわかる「音なし/出す」「音あり/消す」という日本語表記へ変更しました。サービス名や説明文からも不要な英語を減らし、初めて使う人でも迷いにくい言葉へ統一しました。

紙の質感とパンチ穴を表現したスマホ用ビンゴカード
開いたマスを紙のパンチ穴として見分けやすくした参加者カード

実機テストで見つかった4つの大きな問題

この開発では、複数の担当に分けて司会者役、参加者役、仕様照合役、セキュリティ確認役を並行させました。画面を眺めるだけではなく、独立したブラウザで会場作成からゲーム終了まで実際に操作しました。

見つかった問題原因と修正
参加者が開くとChatGPTのログイン画面になるSiteの共有範囲が参加者向けの公開設定になっていなかったため、インターネット上の全員が開ける公開設定へ変更し、未ログイン端末から再確認
抽選後に参加者画面がすぐ反応しない通知と状態取得の経路を分け、版番号による追いつき、長時間待機型の通知、短間隔同期への切替、復帰時の完全同期を追加
穴が開いたことがわかりにくい紙の質感、グレー化、打ち抜き形状、発光、数字の薄い残像を組み合わせて状態差を強化
リーチの場所が伝わらないリーチした5マスを蛍光色の線で結び、重複リーチも表示できるように変更

特に公開範囲は重要です。Sitesの公式ヘルプでも、新しいSiteは最初から一般公開ではなく、公開SiteはChatGPTのワークスペースへアクセスできない人でも利用できる設定にできると説明されています。作成者のブラウザだけで開けても、公開確認にはなりません。

テストは「一人で一画面」では足りない

今回の単体テストでは、ビンゴの12本の成立ライン、カード生成、リーチ、ビンゴ、取消後の再計算、表示名の検証、再接続、リーチ線など55項目を自動確認し、すべて通過しました。

さらに、次のE2E試験シナリオと負荷確認用スクリプトを用意し、手動の本番相当確認と組み合わせました。

  • 司会者1台と、互いに保存領域を共有しない複数の参加者ブラウザ
  • 参加、受付締切、開始、抽選、取消、再接続、終了までの通し操作
  • 同じ抽選操作が並行した場合の重複防止
  • 通知が途切れた後に最新版へ追いつくこと
  • 想定上限の参加者が集中して入る負荷確認
  • 想定人数の端末が同じ状態へ同期する負荷確認

自動テストで通っても、実際のスマートフォンでは文字の見切れ、タップしづらさ、効果音の許可、画面のスリープ、会場の通信品質などが残ります。本番前には、利用予定の端末と回線でも短いリハーサルを行うのがおすすめです。

手順8:Sitesでは「保存」と「公開」を分ける

Sitesの公式開発者ガイドでは、公開は次の2段階に分かれています。

  1. バージョンを保存する:公開候補をビルドし、内容を確認できる状態にする
  2. 保存したバージョンをデプロイする:選んだ対象者がアクセスできる本番版として公開する

すべてのデプロイURLは本番扱いです。そのため、変更するたびにすぐ公開するのではなく、保存版で動作を確認してからデプロイします。

今回の公開確認では、次の順にチェックしました。

  1. 保存版で司会者と参加者の基本操作を確認
  2. 一般公開の対象を設定
  3. 本番へデプロイ
  4. ChatGPTへログインしていない別端末からトップを開く
  5. 会場作成、参加、抽選、再接続を再確認
  6. 独自ドメインを接続し、HTTPSと主要ページを確認

Codexへ修正を頼むときの具体的な書き方

見た目の違和感を直すプロンプト

参加者カードの穴が開いたことが一目でわかりません。未抽選、開いた直後、開いた後、FREE、リーチ、ビンゴを、色だけに依存せず、形、影、文字、動きでも区別してください。紙のビンゴカードらしい白い質感を保ち、開いたマスはグレーの打ち抜き穴に見せてください。

リアルタイム不具合を直すプロンプト

司会者が抽選して数字が確定した直後、複数の参加者ブラウザで最新数字と該当マスが反映されるまでを計測してください。通知、サーバー保存、状態取得、画面更新のどこで遅れているかを特定し、通信断からの復帰と代替同期も含めて修正してください。修正後は独立した複数ブラウザで再現テストしてください。

公開前の確認プロンプト

公開版をログインしていない利用者の視点で確認してください。トップ、会場作成、参加、ゲーム、再接続を実端末相当で操作し、ログイン要求、権限漏れ、管理用情報の露出、検索対象にすべきでないページ、モバイル表示、エラー文言を確認してください。

ポイントは、「いい感じに直して」ではなく、利用者、操作、期待する結果、確認方法まで一度に伝えることです。

AIに任せた部分と、人が判断した部分

Codexは大量のソースを読み、関連箇所をまとめて直し、テストを繰り返す作業が得意でした。一方、次の判断は人が実際に触らないと決まりませんでした。

  • 穴が開いた感覚をどこまで強くするか
  • 紙らしさとデジタル演出をどう両立するか
  • 初めての人にも伝わる言葉になっているか
  • 抽選の間が楽しいか、待たされていると感じるか
  • 参加者にChatGPTへのログインを求めない公開体験になっているか
  • 管理用リンクを誰が保管するか

AIを使うと、人の仕事がゼロになるのではなく、人は「何を良い体験とするか」の判断へ集中できます。コードを書く時間を、実際に遊んで違和感を言葉にする時間へ移せたのが大きな利点でした。

公開記事や開発記で出してはいけない情報

具体的な手法を紹介するときでも、実際のサービスを操作できる情報は公開しません。本記事では次を除外しています。

  • 実際の管理用リンク、参加用リンク、参加コード、読み取りコード
  • 認証情報、端末に保存される識別情報、環境設定値
  • ホスティングやデータベースの固有識別子
  • 開発端末の利用者名、ローカルパス、ブラウザ情報
  • 実在する参加者名、イベント名、メールアドレス
  • 生のログ、通信記録、管理画面のスクリーンショット

実装例を公開するときは、実データをぼかすだけではなく、架空データへ差し替える方が安全です。読み取りコードは見た目をぼかしても解読できる場合があるため、概念図へ置き換えます。

CodexとSitesでWebアプリを作るときのチェックリスト

  • □ 誰が何をするサービスかを一文で言える
  • □ 静的モックではなく必要な保存・同期を明記した
  • □ 状態遷移と禁止操作を実装前に決めた
  • □ サーバー側を正しい状態の基準にした
  • □ 同じ操作の再送と同時操作を想定した
  • □ 通信断と再接続後の追いつきを設計した
  • □ 管理者と利用者で見える情報を分けた
  • □ 複数の独立したブラウザで通し操作した
  • □ 想定人数の負荷試験を用意した
  • □ 保存版を確認してから本番へデプロイした
  • □ ログインしていない端末で公開範囲を確認した
  • □ 管理情報や個人情報が公開物へ入っていない

CodexとChatGPT Sitesに関するよくある質問

プログラミング経験がなくても作れますか?

仕様を言葉にし、画面を触って違和感を伝えるところから始められます。ただし、権限、個人情報、決済、医療情報など、失敗時の影響が大きい機能は専門家の確認が必要です。まずは用途を絞った軽量なWebアプリから始めるのが安全です。

一回のプロンプトで完成しますか?

簡単な紹介ページなら一回で形になることもありますが、複数端末が同期するサービスは、仕様、実装、テスト、実機確認、修正、公開確認の反復が必要です。今回も、同期、公開範囲、カードの見え方などは実際に遊んでから改善しました。

WebSocketを使わないとリアルタイムになりませんか?

要件と実行環境によります。今回は、変更通知を長く待つ接続と、状態の版番号を使った同期、短間隔の代替同期を組み合わせました。重要なのは通信方式の名前ではなく、変更を早く知り、取りこぼしても正しい最新版へ戻れることです。

Sitesへ公開すれば誰でも見られますか?

自動的に誰でも見られるわけではありません。新しいSiteは限定された状態から始まり、ワークスペースやプランで利用できる公開範囲を選びます。一般向けサービスでは、未ログインの別端末から開けることまで確認してください。

「スマホでビンゴ」は無料ですか?

利用者向けの「スマホでビンゴ」は完全無料です。会員登録やアプリのインストールも不要で、公式ページから会場を作れます。

まとめ:AI開発でも、仕様と実機確認が品質を決める

CodexとChatGPT Sitesを使うと、要件整理、実装、テスト、ホスティング、公開までを一つの流れで進められます。今回のような複数端末同期のWebアプリも作れました。

ただし、成功の中心にあったのは魔法の一文ではありません。

  1. 仕様書を唯一の要件として固定する
  2. 状態遷移とデータの正本を先に決める
  3. 通知を取りこぼしても最新版へ戻れる同期を作る
  4. 独立した複数ブラウザで実際に遊ぶ
  5. 違和感を具体的な受入条件へ変えて修正する
  6. 保存版と公開版を分け、未ログイン端末から確認する

実際の完成形を試したい方は、完全無料の「スマホでビンゴ」をご利用ください。司会者は会場を作り、参加者へ読み取りコードを見せるだけで始められます。

スマホでビンゴを無料で始める

参考:OpenAI「Sites」開発者ガイド、OpenAI「ChatGPT Sitesの作成と管理」

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次