【ITニュース解説】Your form needs an uncertain state, not just success and failure
2026年10月30日に「Dev.to」が公開したITニュース「Your form needs an uncertain state, not just success and failure」について初心者にもわかりやすく解説しています。
ITニュース概要
フォーム送信では「成功」「失敗」だけでなく「不確実」な状態も必要だ。サーバーがリクエストを受け取っても、ブラウザに結果が届かない場合、安易な再試行は重複送信を招く。記事では、HTTP応答やJSON内容が条件を満たせば「受信済み」、そうでなければ「不確実」と定義。これにより、重複を防ぎつつ、ユーザーにサーバー処理の不明瞭さを伝える。
ITニュース解説
ウェブサイトのフォームに情報を入力し、送信ボタンを押す経験は誰にでもあるだろう。その際、画面に「送信が成功しました」と表示されたり、「送信に失敗しました、やり直してください」と表示されたりすることが一般的だ。しかし、この「成功」と「失敗」という二分された状態だけでは、システムが直面する様々な複雑な状況を適切に表現できない場合がある。システムエンジニアを目指す上で、このような細部の設計がユーザー体験やデータの正確性に大きく影響することを理解しておくことは非常に重要である。
例えば、あなたがフォームを送信した際、データはサーバーに無事に保存されたにもかかわらず、その結果を伝えるはずのサーバーからの応答(レスポンス)が、何らかの理由でブラウザに届かなかったと想像してほしい。この時、もし画面に「失敗しました、もう一度試してください」と表示されたらどうなるだろうか。ユーザーは素直にもう一度送信ボタンを押すかもしれない。しかし、これではサーバー上で同じ情報が二重に登録されてしまう可能性がある。これは「べき等性」が保証されていないサーバーの場合に起こる問題だ。べき等性とは、同じ操作を何度繰り返しても、その結果が常に同じであることを指す。多くの問い合わせフォームの登録処理は、一度の送信で一つの問い合わせを生成するため、本来はべき等性がない。フォームの送信ボタンを一時的に無効にすることで、ユーザーが同じページで連続してクリックするのを防ぐことはできるが、それは通信エラーによる再送信や、サーバー側での重複処理を防ぐことにはならない。
このような課題を解決するために、フォームの送信状態をより詳細に管理することが提案されている。それは、「ready(準備完了)」、「sending(送信中)」、「received(受信済み)」、そして「uncertain(不確実)」という四つの状態を用いる方法である。この中で特に重要なのは、「received」と「uncertain」の境界を明確に定義することだ。
「received」、つまりサーバーがリクエストを確かに受け付けたと判断する基準は、単に通信が成功したか否かだけでは不十分である。通常、ウェブ通信ではHTTPステータスコードが用いられ、200番台のコードは通信の成功を示す。しかし、これはあくまで技術的な通信の成功であり、アプリケーションが意図するビジネスロジックが成功したことを意味するわけではない。例えば、JavaScriptでResponse.okというプロパティを確認しても、それはHTTPステータスが200〜299の範囲にあるかどうかのチェックに過ぎない。重要なのは、サーバーからのJSON形式の応答ボディの中身を適切に解釈することである。記事の例では、HTTPレスポンスが成功し、かつJSONボディの中にok: trueという記述があり、さらに特定の形式(UUID形式)の一意な参照ID(reference)が含まれている場合にのみ、「received」と判断する。この参照IDは、リクエストが一意に識別されたことを示すもので、後の追跡にも役立つ。つまり、「received」という状態は、単にデータがサーバーに到達しただけでなく、アプリケーションとしてそのリクエストが受け入れられ、処理が始まる準備が整ったことを意味するのだ。
一方、「uncertain」という不確実な状態は、様々な理由でサーバーからのリクエストの受付が確認できなかった場合に用いられる。例えば、サーバーからの応答がJSON形式として解析できなかったり、通信中に接続が途切れてしまったり、HTTPステータスは成功と示されたものの必要な情報(ok: trueや参照ID)がレスポンスボディに含まれていなかったりするケースである。これらの状況では、クライアント側ではサーバーにデータが保存されたかどうかを確実に判断できない。この状態では、システムは自動的に再送信を試みるべきではない。なぜなら、サーバー側ではすでにデータが保存されている可能性があり、安易な再送信は重複登録を引き起こすリスクがあるからだ。代わりに、ユーザーに対しては「送信の確認ができませんでした」といったメッセージを表示し、必要であれば電話番号やメールアドレスなど、別の連絡手段を提示することが適切である。これは、システムが「未確認である」という事実を正直に伝える、非常に保守的なアプローチと言える。
ユーザーが再試行を許可されるのは、特定の明確なエラーが発生した場合に限られる。例えば、入力されたデータが形式的に正しくない(バリデーションエラー)、認証が失敗した(検証エラー)、送信データ量が大きすぎる(サイズエラー)、短時間に多すぎるリクエストがあった(レートリミットエラー)など、サーバー側で「これは受け付ける前に拒否された」と確実に判断できるエラーの場合である。これらのエラーは、クライアントがデータを修正すれば再試行できるため、フォームは「ready」状態に戻される。しかし、このエラーの種類や条件も、個々のサーバーAPIの「契約」(API仕様)によって異なるため、他のサービスの設定を安易に流用すべきではない。Stripeのような一部のAPIでは、「べき等性キー」という仕組みを使って、同じリクエストが複数回送信されても一度しか処理されないように設計されているが、これは特別な設計であり、一般的な問い合わせフォームに自動的に適用されるものではない点に注意が必要である。
このような複雑な状態管理を実現するには、徹底したテストが不可欠である。単にフォーム送信が成功する「ハッピーパス」だけでなく、ユーザーが同時に複数回クリックした場合、意図的に不正なレスポンスを返した場合、必要な参照IDがない成功レスポンス、あるいは前述のような事前承認エラーなど、あらゆるシナリオを想定してテストを行う必要がある。テストは、クライアント側のコードが、様々なサーバーからの応答に対して意図した通りに振る舞うことを検証するものであり、これによりシステムの信頼性が高まる。
最後に、技術的な「受信」と、ビジネス上の「成果」を混同しないことが重要である。「受信済み」はあくまで、サーバーがリクエストを受け付けたというエンジニアリング上の事実を示すものであり、それが顧客の獲得や問い合わせへの対応開始を意味するわけではない。例えば、問い合わせが「受信済み」となっても、それはまだ営業担当者が内容を読んだり、返信が行われたりする前の段階である。UIに表示されるメッセージは、その裏にある確かな「証拠」に基づいていなければならない。もしその証拠が不足している場合、インターフェースはどう振る舞うべきかを常に問いかける必要がある。この「uncertain」という状態は、単なる緑色の成功アニメーションよりも、システムと外部サービスとの連携における潜在的な問題点や、考慮すべき設計上の詳細について、より多くの貴重な情報と洞察を与えてくれるのだ。