2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

文化祭クイズラリーアプリ開発で起きた「正解が不正解になる」不具合の振り返りと学び

2
Last updated at Posted at 2025-12-10

はじめに

今年の文化祭で、研究室メンバー4人と一緒に クイズラリーアプリ を開発しました。
チーム内の担当はざっくり次のとおりです。

  • インフラ担当(AWS):自分
  • バックエンド担当(Flask API・DB 管理):メンバー2名
  • フロントエンド担当(Flutter):手が空いた人(私+他2名で、AIも活用しながら高速に作成)

…と書くと順調に進んだように見えるのですが、
本番直前に「正解を入力しても不正解になる」という致命的バグが発覚。
最終的には、泣く泣く インフラもバックエンドも外して、フロント単体アプリとして運用 することになりました。

この記事は、「なぜそんなことが起きたのか?」を備忘録も兼ねてまとめたものです。

1. 当初のアーキテクチャ

このアプリは UI(ブラウザ)とサーバーが別々に進捗を持つ設計 でした。

UI(Flutter Web):

Cookie にユーザー状態を保存。

  • userId
  • solvedPinIds(正答済み問題リスト)

地図上の表示は、この solvedPinIds を基準にしていました。

サーバー(Flask + PostgreSQL):

  • 正解履歴を CorrectAnswer テーブルに保存
  • 提供 API
    • /api/quiz:[GET] 問題の取得, [POST] 正誤判定(正解済みなら 400)
    • /api/createID:[GET] userId 発行
    • /api/correctAnswerRate:[GET] 問題の正答率の取得

▼ データの流れ(簡略)

  1. 起動時:アプリが Cookie を読み込み、進捗状況(userId や solvedPinIds)を復元する。
  2. 回答時:UI から userId・quizId・回答内容が API(サーバー)へ送られる。
  3. 判定
    • サーバーは DB(CorrectAnswer テーブル)を参照し、その userId がその問題を既に正解済みか確認する。
    • 既に正解済みならエラー(400)を返す。
    • 未回答の場合のみ答え合わせを行い、正解の場合に限り DB に「正解済み」の記録を追加する
  4. 更新:UI はサーバーの判定を受け取り、正解の場合は Cookie の solvedPinIds を更新する。

つまり UI が参照する進捗(Cookie)と、サーバーが参照する進捗(DB)が独立して存在する設計 だったということです。

2. 発生した問題:正しい答えが“不正解”扱いになる

本番直前に、次のような矛盾が発生しました。

  • UI:すべて未クリアに見える
  • サーバー:その userId は「既に正解済み」と判断
  • 結果:正しい答えでも 400(重複扱い) → UI には「不正解」表示

UI とサーバーが別々の現実を見ている状態でした。

3. Cookie による進捗管理の不整合を疑ったが説明がつかなかった

バグ発生時、まず疑ったのは「solvedPinIds の Cookie 側だけ進捗が初期化されたのでは?」という点でした。
ただし、このアプリでは userIdsolvedPinIds を 1つの JSON として保存していたため、本来は solvedPinIds だけ消えるような “部分的な破損” は起こらない設計になっています。
JSON が壊れた場合は Cookie 全体の読み込みが失敗するからです。

しかし実際には、

  • UI 上ではすべて未クリア(=進捗ゼロ)に見えた
  • 一方でサーバーには、過去の userId が送られ続けていた

という「一部だけ初期化されたように見える状態」が発生していました。

他にもFlutter Web のキャッシュや Service Worker が何かしらの悪影響を与えている可能性も考えられますが、結局何が原因なのかは最後までわかりませんでした。ただ、UI(Cookie)とサーバー(DB)が別々に進捗を持っていたことで状態がずれた点が、問題の根本だったと考えています。

ログインの手間を省くためにCookieを導入しましたが、今思えば、Cookie には userId だけを保存し、進捗の参照はバックエンドに一本化すべきでした。

6. 最終判断:バックエンドを外し、フロント単体運用へ

時間的制約もあり、次の理由からバックエンドとインフラを外して、フロント単体アプリとして運用することになりました。

  • 原因が特定できず、構造的にも不整合が再発しうる状態だった
  • UI 単体でもイベント運営には支障がない

結果、文化祭の運用は問題なく行えました。

おわりに

今回のバグは最後まで特定できませんでした。
それでも、UI とサーバーの状態が一致しない設計がどれほど危険かを実感できたことは大きな収穫でした。

今回のような問題を繰り返さないためにも、今後はシステム全体の仕様や、テスト・デバッグ方針をチームでしっかり共有し、不整合が起きにくい仕組みづくりを進めていきたいと思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?