画像アップロードで「保存できたのに使えない」を潰した話

画像保存は成功しているのに現場では使えない。URL生成、デプロイ後実行、一覧探索の3層で詰まった原因を分離して直した実装ログです。

はじめに

記録時点: 2024-09〜2026-01

この時期の画像機能は、保存APIの成功率だけを見ると問題がないように見えていました。 ただ運用では「保存できたのに使えない」という問い合わせが繰り返し発生し、 編集フロー全体としては失敗扱いになっていました。

この記事で扱う範囲

  • URL生成の不整合で発生した表示崩れ
  • デプロイ直後のみ発生する取得エラー
  • 画像件数増加時の探索不能(見つけにくさ)

個別不具合の話に見えますが、実際は「保存成功」を過信した設計の見直しです。

症状は3種類に分かれていた

1. アップロード成功なのにプレビューが出ない

特定のファイル名でのみ画像URLが壊れ、表示できないケースがありました。 利用者視点では「保存は成功したのに画像だけない」ため、失敗理由が伝わりません。

2. ローカルでは再現せず、デプロイ後だけ取得APIが落ちる

開発環境で再現しないため、最初はデータ不整合を疑いました。 実際は、デプロイ時の参照構成に依存したエラーでした。

3. 画像はあるのに一覧で見つけられない

件数が増えると初回読み込みが重くなり、探索の手数も増えます。 機能としては成立していても、運用では「使えない」に近い状態です。

切り分けの順番

この回で効いたのは、症状の頻度順ではなく責務順で切り分けたことです。

  1. URL生成の契約をフロント/バックで突き合わせる
  2. デプロイ後の実行形とローカル実行形の差分を見る
  3. 一覧UIを性能ではなく探索完了までの手数で評価する

この順番にすると、見た目上つながって見える不具合を別問題として扱えます。

実装で変えたポイント

1. URL生成ルールを共通化した

まず、S3キーをURLへ載せる時点で必ず同じ正規化を通すようにしました。

export function toAssetUrl(baseUrl: string, objectKey: string): string {
  const normalizedBase = baseUrl.replace(/\/$/, "");
  const encodedKey = encodeURIComponent(objectKey).replace(/%2F/g, "/");
  return `${normalizedBase}/${encodedKey}`;
}

「保存時は問題ないが表示時に壊れる」症状は、この共通化で大きく減っています。

2. デプロイ後の実行形に合わせて参照構成を修正した

共通化した処理の置き場所が、デプロイアセットでは解決できない経路になっていました。 見た目の共通化より、実行時に確実に読める配置を優先して整理しました。

3. 一覧を分割取得に切り替え、探索導線を追加した

無限スクロールを導入し、重複を除外しながら継ぎ足し表示する形へ変更しました。

export function mergeUniqueById(current: ImageItem[], next: ImageItem[]): ImageItem[] {
  const map = new Map<string, ImageItem>();
  for (const item of current) map.set(item.id, item);
  for (const item of next) map.set(item.id, item);
  return [...map.values()];
}

加えて、ファイル名フィルタ・ソート・表示切り替えを入れ、 「表示できる」ではなく「見つけて選べる」状態へ寄せました。

検討して見送った案

1. 取得失敗時に毎回再試行で吸収する

一時的には改善しますが、根本原因を隠して監視を難しくするため見送りました。

2. 一覧を常に全件取得してクライアントで完結させる

件数増加時の初回体験が悪化するため採用していません。

3. URL崩れを画面側の例外処理だけで吸収する

表示層で吸収すると、他画面で同じ不具合が再発するため契約側で直しました。

回帰確認で固定した観点

観点 期待値
空白/記号を含むキー URLに空白が残らない
デプロイ直後の画像取得 参照エラーで落ちない
追加読み込み 重複表示が出ない
フィルタ/ソート変更後 選択状態が壊れない
アップロード直後 同ダイアログで即選択できる

この回の判断が次に効いたこと

「保存成功」ではなく「使える状態」までを完了条件にする考え方は、次の記事にもつながっています。

まとめ

今回の改善は、画像表示の不具合修正ではなく、画像利用フローの再設計でした。 URL契約、実行環境、探索導線の3層を分けて直したことで、 「保存できたのに使えない」状態を実運用で減らせています。

公開リンク