この記事で扱うこと
一日で 二つ を扱った。タッチ画面でブロックのドラッグ・カラムのリサイズ・hover でしか見えなかったコントロールが壊れる問題、そして画像最適化が切れたアプリで一覧のサムネイルが原本画像をそのまま受け取っていた問題。各作業がどんな状況で何を目標にどう解けて何を学んだかを、作業ごとに整理する。
一日のまとめ
| 作業 | 何をしたかったか | やったこと | 結果 |
|---|---|---|---|
| ジェスチャ | タッチで引いてもページが一緒にスクロールしないように | pointerType 分岐(マウス即時/タッチ long-press·threshold)、pointercancel も pointerup と同じ整理経路、つまみに touch-action:none、hover がない機器はコントロール常時表示 | スクロール奪取・contextmenu・ハンドルのちらつき・ドラッグ固着を解消 |
| 転送量 | 画像を必要なサイズ分だけダウンロードさせる | サーバプロキシで原本 fetch → WebP 変換 → CDN + インメモリ 2 層キャッシュ、miss は tee() でストリーミング | 同じ画像の再要求時に転送バイトが大きく減少 |
| 安全 | プロキシがどんな URL でも代わりに受けないように | 許可ホストだけ fetch する SSRF ガード、署名クエリを外した pathname キャッシュキー | 内部網アドレスを遮断、キャッシュキーを安定化 |
| 体感 UX | 本画像の前にぼやけたプレビューで場所を予約 | 最初のカードに LQIP を SSR で付けつつ stale-while-populate(応答が先、キャッシュは後ろで埋める)、preload href = img src | 空き場所のガタつき減少、preload の無駄なし |
1. モバイルのポインタジェスチャ
前提知識(概念)
- Pointer Events: マウス・タッチ・ペンを一つのイベントモデルにまとめたブラウザ API。イベントごとに
pointerType(mouse/touch/pen)が付く。 touch-action: この要素でブラウザがスクロール・拡大などの既定タッチ動作をどれだけ持っていくかを決める CSS。noneならブラウザは手を出さず、我々のコードがジェスチャを完全に持つ。
どんな状況だったか
デスクトップ用のポインタドラッグをタッチにそのまま移すと症状が幾重にも重なる。スクロールにジェスチャを奪われ、長押しするとブラウザの右クリックメニュー(contextmenu)が出て、hover でしか見えなかったハンドルがタップごとにちらつき、手を離す pointerup の代わりにブラウザがジェスチャを横取りした pointercancel だけ来るとドラッグ状態が固着する。
中心の作業
pointerType 分岐(マウスは即時/タッチは long-press·threshold)、pointercancel も pointerup と同じ整理経路、つまみに touch-action:none、hover がない機器はコントロール常時表示で直した。結果としてスクロール奪取・contextmenu・ハンドルのちらつき・ドラッグ固着が解消された。
教訓
- タッチジェスチャはデスクトップポインタのコピペではない。
pointerType分岐・touch-action・pointercancelの整理をそれぞれ明示しなければならない。
2. サーバ画像プロキシ
前提知識(概念)
- 画像プロキシ: 原本をそのまま送らず、サーバが中間でサイズ・フォーマットを変えて(トランスコーディング)より軽く届けるサーバ。
- SSRF (Server-Side Request Forgery): サーバが代わりに URL を fetch してくれるとき、攻撃者が内部網アドレスを入れてサーバを迂回通路として悪用する脆弱性。
- LQIP (Low-Quality Image Placeholder): 本画像が来る前にちょっと見せる、とても小さくぼやけたプレビュー画像。
- LCP (Largest Contentful Paint): ビューポートで最大のコンテンツが描かれるまでの時間。性能指標の一つ。
どんな状況だったか
画像最適化が切れた環境ではフレームワークがリサイズ・フォーマット変換をしないので、小さなサムネイルの場所にも 原本まるごと が降りてくる。転送バイトが不必要に大きい。しかも画像が到着する前に空き場所がガタつくと体感速度が悪く見える。サーバが代わりに URL を fetch することになるので、どんな URL でも受けると SSRF 危険も生じる。
中心の作業
- 転送量: サーバプロキシで原本 fetch → WebP 変換 → CDN + インメモリ 2 層キャッシュ、miss は
tee()でストリーミング。同じ画像の再要求時に転送バイトが大きく減った。 - 安全: 許可ホストだけ fetch する SSRF ガード、署名クエリを外した pathname キャッシュキーで内部網アドレスを遮断しキャッシュキーを安定化した。
- 体感 UX: 最初のカードに LQIP を SSR で付けつつ stale-while-populate(応答が先、キャッシュは後ろで埋める)、preload href = img src で空き場所のガタつきを減らし preload の無駄をなくした。
教訓
- 画像最適化が切れた環境ではサーバプロキシが最大の利得を与える — 転送バイトが減るのが肝。
- 性能比較は転送バイトが最も再現可能な証拠だ。localhost の LCP 計測はネットワーク遅延(RTT)がなく、実デプロイの CDN 利得を過小評価しやすい。
- ぼやけた placeholder(LQIP)はスコア用ではなく体感 UX 用だ。ブラウザは低情報の blur を LCP 候補から外すので、LCP の数字を動かすには本画像の軽量化が先だ。
→ サーバ画像プロキシ — トランスコーディング・キャッシュ・ストリーミング · SSRF URL fetch プロキシガード · LQIP blur placeholder — SSR stale-while-populate