37 ポイント 投稿者 GN⁺ 2025-11-03 | 2件のコメント | WhatsAppで共有
  • URLの構造は単なるアドレスを超えて、アプリケーションの状態を保存・復元する手段として機能する
  • PrismJS のダウンロードページのように、1つの URL だけでテーマ・言語・プラグイン設定を完全に再現できる事例を提示
  • パス、クエリパラメータ、フラグメントなど各構成要素が、階層的なナビゲーション・フィルタリング・クライアントナビゲーションなど多様な状態を表現する
  • 検索フィルタ、ページネーション、表示モード、日付範囲などは URL に含めるのに適しており、機密情報や一時的な UI 状態は不適切
  • よく設計された URL は共有性・予測可能性・キャッシュ効率を高め、Web アプリケーションの信頼性とユーザー体験を強化する

URLの可能性

  • URL は単なるリソースのアドレスではなく、ユーザーインターフェース(UI) であり、状態コンテナとして機能する
    • 共有、ブックマーク、ブラウザ履歴、ディープリンクなどで状態を自動的に保持する
    • 1991年から Web の基本的な状態管理メカニズムとして機能してきた
  • URL の各構成要素は異なる種類の状態を表現する
    • パス(Path) : 階層的なリソース探索 (/users/123/posts)
    • クエリ(Query) : フィルタ・オプション・設定 (?theme=dark&lang=en)
    • フラグメント(Fragment) : ドキュメント内の位置や SPA ルーティング (#features, #/dashboard)
  • Text Fragments 機能により、ページ内の特定テキストへ直接リンクできる

クエリパラメータのパターン

  • 区切り文字(delimiter) で複数の値を1つのキーに入れる方式 (?tags=frontend,react,hooks)
  • ネストしたデータを JSON または Base64 にシリアライズ (?config=eyJyaWNrIjoicm9sbCJ9==)
  • ブールフラグはキーの存在有無で表現 (?mobile)
  • 配列表記(bracket notation)tags[]=frontend&tags[]=react 形式で複数値を表現
    • Node の qs や Express ミドルウェアなどでは自動認識されるが、標準化はされていない
  • 重要なのは一貫性を保つこと

URLによる状態表現の事例

  • PrismJS: URL ハッシュにテーマ・言語・プラグイン設定全体を保存
  • GitHub: #L108-L136 で特定のコード行範囲を強調
  • Google Maps: 座標・ズームレベル・地図タイプを URL に含める
  • Figma: キャンバス位置・ズーム・選択要素など作業コンテキストを URL で共有
  • ECサイト: フィルタ・並び替え・価格帯を URL に含めて検索状態を復元

フロントエンドエンジニアリングのパターン

  • URL に含めるのに適した状態
    • 検索語、フィルタ、ページ・並び替え、表示モード、日付範囲、アクティブタブ、UI 構成、機能フラグ
  • URL に不向きな状態
    • パスワード・トークンなどの機密情報、一時的な UI 状態、未保存入力、大容量データ、高頻度の状態
  • 判断基準: 他のユーザーが同じ URL をクリックしたとき、同じ状態を見るべきか

JavaScript実装

  • URLSearchParams API でクエリパラメータの読み書きが可能
  • pushState で新しい履歴項目を追加し、replaceState で現在の項目を更新
  • popstate イベントでブラウザの戻る操作時に UI を復元

React実装

  • React Router の useSearchParams フックで URL 状態を簡潔に管理
    • パラメータの読み取り・更新時に URL と UI を自動同期する

URL状態管理のベストプラクティス

  • デフォルト値は URL に含めない (?theme=dark だけを保持し、デフォルト値はコード側で処理)
  • デバウンスで入力中の URL 過剰更新を防止 (lodash.debounce を活用)
  • pushState vs replaceState
    • pushState: フィルタ変更・ページ移動など、戻れるべき状態
    • replaceState: 検索入力など細かな修正

URLを契約(Contract)として見る

  • よく設計された URL はアプリケーションとユーザーの間の明示的な契約として機能する
    • 公開/非公開、クライアント/サーバー、共有/セッション状態の境界を明確にする
  • 可読性の高い URLは意図を説明し、人にも機械にも理解しやすい
    • example.com/products/laptop?color=silver&sort=price のような形は意味伝達に有利
  • キャッシュ効率の向上
    • 同じ URL は同じリソースとみなされ、キャッシュヒット率が上がる
    • クエリパラメータでキャッシュのバリエーションを制御できる
  • バージョン管理と実験
    • ?v=2, ?beta=true, ?experiment=new-ui などで API バージョン・A/B テストを区別

避けるべきアンチパターン

  • SPA でメモリ内状態だけを保持して、リロード時に状態を失う
  • 機密情報を URL に含める (?password=secret123)
  • 不明確なパラメータ名 (?foo=true&bar=2 ではなく ?mobile=true&page=2)
  • 複雑な JSON を Base64 でエンコードして過度に長い URL を生成
  • URL 長の制限を超える(ブラウザ・サーバー・CDN に制約がある)
  • 戻るボタンを無効化する (replaceState の乱用で発生)

結論

  • 良い URL はコンテンツを指し示す以上に、ユーザーとアプリケーションの間の対話を表現する
  • URL は意図・コンテキスト・共有可能性を運ぶ、最も古く洗練された状態管理手段である
  • Redux・MobX・Zustand・Recoil など複雑な状態管理ツールは存在するが、
    URL という基本機能を忘れないことこそが、本当の Web の強みである
  • リロード時に状態を失うアプリは、Web の本質的な特性を見落としている

2件のコメント

 
GN⁺ 2025-11-03
Hacker Newsのコメント
  • コードレビューの際、できるだけ多くの状態(state) をURLに保存するようにしている
    リロード後にまったく別の場所へ移動したり、共有したURLが見当違いの画面を表示したりするのは、ユーザーにとって侮辱的ですらある
    このやり方は開発速度を落とすが、チーム内でUXへの意識が高まり、ビューにどれだけ多くの状態を持たせているかを明確に把握できる
    URLが一種の公開APIになって制約が生まれるという懸念もあるが、ほとんどのURLは短期的にしか使われないため、大きな問題ではないと思う
    必要なら、読み込み時に古いURLを新しいURLへマイグレーションするコードで対処できる

    • このアプローチは気に入っているが、ブラウザの履歴オートコンプリートのせいで望まない状態が呼び出されることがある
      パス(path)の代わりにクエリパラメータを使うほうが少し良いと思う
    • 私が使っている業務用Webアプリは独自の「戻る」ボタンを作っていて、ブラウザの戻る機能が完全に壊れている
      ユーザーの立場では、「戻る」という言葉はブラウザボタンと結び付いているので混乱する
      リロードで状態が初期化されるほうがまだ腹立たしくない。「リロード = 最初からやり直し」という認識があるからだ
    • サーバーレンダリングのページなら、リロード時にスクロール位置が自動で復元される
      JSですべてを処理すると、こうした基本機能が微妙に壊れる
    • URL設計はUXデザインの一部だと思う
      だが、これまで30人以上のUXデザイナーと仕事をしてきても、URLについてのガイドを受けたことはない
    • Webの進化とともにリロードの意味は状況ごとに変わってきた
      特にモバイルではページを初期状態に戻すのが難しく、リロードが最も手っ取り早い解決策になる
      無限スクロールや複雑なフィルターUIでは、URLに状態が多いほど初期化がさらに面倒になる
      すでにUXに不満がある状況でURLまで整理しなければならないなら、それはユーザーにとって二重のストレスだ
  • デジタルリテラシーの高い人でさえ、URLとDNSの理解度は低いと感じる
    フィッシングの危険を減らし、URLパラメータ(?t=_, utm_)の意味を理解し、共有前に個人情報を取り除けるようであるべきだ
    HTTPSの鍵マークが「信頼」を意味しないことも知っておくべきだ

    • しかしブラウザはデフォルトでURLを隠したり省略したりし、企業はQRコードや検索語だけを宣伝するため、教育が難しい
  • URLを状態コンテナとして使うと内部構造が露出し、バージョン管理が必要になる
    ブラウザ間の互換性や認証フローでも問題が起こり得る
    それでも、コマンドライン引数のようにできるだけ多くの状態をURLに露出させようとしている
    ただしこれは意図的なトレードオフであって、無知や経験不足のせいではない

  • 古いライブラリだが今でも有用な Rison を勧めたい
    JSONをURLにきれいに保存でき、ElasticのKibanaでも使われている
    例: http://example.com/service?query=q:'*',start:10,count:10

    • まさにこういうものを探していた! 昔は自分で間に合わせのものを作っていたが、これはずっと標準的で整理された方法に見える
  • システムが進化すれば状態構造も変わるため、URLに状態を入れると進化が制約される
    URLは基本的に永続的な文字列だからだ
    その代わり、URLを一種のプロトコルと見なし、状態をエンコード・デコードする方式が適切だと思う
    単純なページなら、状態全体をURLに載せることも可能だ

    • 長期間維持されるコンテンツ(例: ブログ記事)では、URLによる状態保存が有用だ
      しかしフィードのように「リロード時に最新状態へ戻るべきか?」といったユーザーの期待値によって変わる
    • バージョン管理を導入すれば、こうした問題を緩和できる
  • URL長の制限はブラウザ・サーバー・CDN・検索エンジンの設定によって異なるが、通常は2000文字以下
    この制限内でどれだけ多くの状態を持たせられるか、あるいは別のアプローチが必要かを考えさせられる

    • ドメインを除く各文字には66種類(英大文字・小文字、数字、特殊文字 - . _ ~)が使えるので、情報密度はかなり高い
  • draw.ioは状態全体をURLに保存して共有できる
    ダイアグラムデータがBase64でエンコードされ、1つのリンクで完全に復元できる
    ただし、これが「state container」の定義に当てはまるのかは確信がない

  • 私はセルフホストアプリでhash routing (#/dashboard) を使っている
    サーバー側のURLリライト(.htaccessなど)が不要なので、完璧ではないにせよデプロイ環境の制約を減らせる

  • 最新のMicrosoft Teamsではすべての画面が1つのURLで処理されるため、ブックマーク不可になっている
    特定のチームやチャンネルを直接開けず、とても不便だ

  • HATEOASは名前が良くないせいで注目されにくいが、結局はWebの基本概念だ

    • ユーザーの立場では、リンクをたどってフォームを送信すること自体がHATEOASだ
      しかしサーバーとクライアントの両方を制御する環境では、追加の複雑さが生じるだけだ
      特にクライアントが依然としてエンドポイント構造を知る必要があるなら、URLを不透明にするだけになってしまう
    • この話題は実際にはHATEOASと直接の関係はない。どちらもURLを使うが、HATEOASは状態保存ではなくナビゲーション構造に関するものだ
    • 冗談だが、結局HATEOASには「シリアル化されたフォーマット(cerealization) 」という言い方のほうが合っている
 
ndrgrd 2025-11-03

タブのスリープ機能をよく使うのですが、URLを固定してひとかたまりで動くWebアプリは、スリープに入ると情報が消えてしまいます。

しかも、そういうWebページはどれも重いので、スリープを無効にするわけにもいきません。