ローディングスピナーは「敗北」である。サーバーを待たずに完了させる「Optimistic UI」の実装論

「ボタンを押したのに、グルグル(ローディング)が回って待たされる」

私たちはこれを当たり前だと思っていますが、
ユーザーにとっては「人生の貴重な時間を奪われている」のと同義です。

Amazonの調査では、表示速度が0.1秒遅れるだけで売上が1%下がると言われています。
0.1秒の「待ち」は、ビジネスにおける損失なのです。

では、どうすれば「待ち時間ゼロ」を実現できるのか? サーバーを光の速さにする必要はありません。

「見せ方(UI)」を変えるだけでいいのです。

本記事では、InstagramやTwitterが採用している「Optimistic UI(楽観的UI)」の概念と、React/Next.jsを用いた具体的な実装パターンを解説します。


目次

1. 心理学:「サーバーの正解」より「ユーザーの体感」

従来のWeb開発の常識はこうでした。
「Pessimistic UI(悲観的UI)」

  1. ユーザーがボタンを押す。
  2. ローディングを表示する(ぐるぐる…)。
  3. サーバーに通信し、DB書き込み完了を待つ。
  4. 成功が返ってきたら、画面を更新する。

これはエンジニアにとっては誠実ですが、UXとしては**「サーバーの都合でユーザーを待たせている」**状態です。

対して、今のトレンドはこうです。
「Optimistic UI(楽観的UI)」

  1. ユーザーがボタンを押す。
  2. 即座に「成功した」と画面を切り替える。
  3. 裏でこっそりサーバーと通信する。

「もし失敗したらどうするの?」と思うでしょう。その確率は(設計によりますが)1%未満です。
99%の成功ケースのために、全員を待たせる必要はない。

これがOptimistic UIの思想です。


2. 実装編:Reactで書く「未来の先取り」

言葉だけではイメージしづらいので、具体的なコード(React)で比較してみましょう。 「いいねボタン」を押す処理を例にします。

❌ 従来の書き方 (悲観的UI)

サーバーからのレスポンス(await)を待ってから、State(画面)を更新しています。
これが「体感の遅さ」の正体です。

例)JavaScript

const handleLike = async () => {
  setIsLoading(true); // 1. ローディング開始
  try {
    // 2. サーバー通信 (ここで数秒待たされる!)
    await api.post('/like', { id: postId });
    
    // 3. 成功してから画面更新
    setLiked(true);
  } catch (err) {
    alert('エラー');
  } finally {
    setIsLoading(false); // 4. ローディング終了
  }
};

✅ Optimistic UIの書き方 (楽観的UI)

API通信の結果を待たず、先にStateを更新しています。

例)JavaScript

const handleLikeOptimistic = () => {
  // 0. 現在の状態をバックアップ (ロールバック用)
  const previousState = liked;

  // 1. サーバーを待たず、即座に「いいね!」にする (爆速UX)
  setLiked(true);

  // 2. 裏側で通信 (Fire-and-Forget)
  api.post('/like', { id: postId })
    .catch((err) => {
      // 3. 万が一失敗したら、しれっと元に戻す (ロールバック)
      console.error('Like failed', err);
      setLiked(previousState);
      
      // 必要ならToast通知などで「反映できませんでした」と伝える
      showErrorToast();
    });
};

このコードにより、ユーザーはボタンを押した瞬間にフィードバックを得られ、
「このアプリ、サクサク動くぞ!」という快感を感じます。


3. プロの道具箱:SWR / TanStack Query の活用

実務でOptimistic UIを実装する場合、上記のように手動でState管理をすると複雑になりがちです。 モダンなフロントエンド開発では、SWRTanStack Query (React Query) というライブラリを使うのが標準です。

これらには optimisticData という専用のオプションが用意されています。

例)JavaScript

// SWRを使った例
import useSWR, { mutate } from 'swr';

const { data } = useSWR('/api/user');

const updateProfile = async (newName) => {
  // mutate(キー, 新しいデータ, 再検証するか)
  mutate('/api/user', { ...data, name: newName }, false); // ★ここがOptimistic UI

  // 裏でAPIを叩く
  await api.updateName(newName);
  
  // 完了したら正しいデータを再取得して整合性をとる
  mutate('/api/user');
};

4. アーキテクチャとの結合:AWS SQS との相性

このフロントエンド技術は、以前の記事で解説した「AWS非同期ログ基盤(SQS)」と組み合わせることで最強の効果を発揮します。

  1. Frontend (Optimistic): 画面を即座に完了させる。
  2. Network (Fire-and-Forget): APIリクエストを投げるが、レスポンスを待たない(あるいは202 Acceptedだけ受け取る)。
  3. Backend (SQS): AWS側でリクエストをキュー(行列)に入れ、非同期でゆっくり処理する。

この構成なら、「フロントエンドは爆速」かつ「バックエンドはスパイクに強い」

という、攻守最強のアプリケーションが完成します。


5. 導入してはいけないケース (注意点)

Optimistic UIは魔法ですが、万能ではありません。以下のケースでは使用を避けてください。

  • 決済処理: 「購入完了」と出した後に「カードエラーでした」と戻すのは、ユーザーの信頼を著しく損ないます。
  • 在庫確保: チケット予約など、ダブルブッキングが許されない処理。

逆に、以下の機能は今すぐOptimistic UIにすべきです。

  • いいね / ブックマーク
  • ToDoリストの追加・削除
  • 下書き保存
  • カートへの追加(※在庫チェックが厳密でない場合)

まとめ:UXは「技術」で作れる

「サイトが重い」「反応が遅い」 これは、回線速度の問題ではありません。

実装のアプローチ(設計思想)の問題です。

サーバーの処理が終わるのを、律儀に待つ必要はありません。

「成功を信じて、先に進める」

この楽観的なアプローチこそが、ユーザーを待ち時間のストレスから解放し、プロダクトの価値を最大化します。


👇 【あわせて読みたい】「裏側」はどうなっている?
【AWS】UXを爆速にする「非同期処理」アーキテクチャ

フロントエンドが「楽観的」に投げたデータを、バックエンドで確実に受け止める「SQS × Lambda」の構成を解説しています。

👉 記事を読む:AWSによる非同期ログ基盤の構築手法

👇 【キャリア】UIとインフラ両方を設計できる人材へ
【年収1000万への道】「機能を作る人」から「ビジネスを救う人」へ

Next.js (フロント) と AWS (インフラ) の両方を理解し、最適なUXを設計できるアーキテクチャ・スキル。 それをどうキャリアに活かすか解説します。

👉 記事を読む:次世代アーキテクトのキャリア論

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次