【ITニュース解説】Syncing Multiple Tabs in a Browser Application
2025年09月27日に「Dev.to」が公開したITニュース「Syncing Multiple Tabs in a Browser Application」について初心者にもわかりやすく解説しています。
ITニュース概要
ブラウザで複数タブのWebアプリを開く際、認証情報の同期は重要だ。一方のタブでログイン・ログアウトしても、他のタブが古い情報を使いエラーとなる問題を、`localStorage`と`StorageEvent`で解決する。認証変更時にデータをクリアし、ページを再読み込みすることで、全タブの認証状態を正しく同期させる。
ITニュース解説
ブラウザで同じウェブアプリケーションを複数タブで開いた際に、それぞれのタブが同期されずにバラバラの状態になってしまうという問題は、多くのウェブアプリケーションで発生しうる。たとえば、オンラインバンキングのサイトで、あるタブでログイン中に別のタブで同じサイトを開こうとすると、最初のタブが「セッションが切れました」と表示されたり、自動的にログアウトされてしまったりすることがある。これはまさに、タブ間で認証情報などの状態が同期されていないために起こる典型的な例だ。
ウェブアプリケーションでは、ユーザーがログインしているかや、表示されているデータなど、さまざまな状態を管理している。しかし、同じアプリケーションを複数タブで開いた場合、それぞれのタブが独立して動作しようとするため、あるタブでの操作が他のタブに自動的に反映されないことがある。特にユーザーの認証情報に関わる場合、この状態の不一致は大きな問題となる。
ある開発者が直面したのは、まさにこの認証情報の同期に関する問題だった。彼の開発したアプリケーションでは、ユーザーが複数のタブでアプリケーションを開いている状態で、いずれか一つのタブでログアウトしても、他のタブはまだユーザーがログインしているかのように振る舞い続けていた。具体的には、ログアウトしたタブ以外のタブでは、すでに無効化された古いアクセス認証トークンを使ってサーバーにAPI(アプリケーション・プログラミング・インターフェース)リクエストを送り続けていたため、サーバーからの応答はエラーとなり、アプリケーションは正しく機能しなくなった。この場合、ユーザーは手動で各タブを再読み込みするまで、操作を続けることができず、ユーザー体験を著しく損なうだけでなく、アプリケーションの信頼性にも関わる問題だった。
この問題を解決するための基本的な考え方は、非常にシンプルだ。複数のタブ間で情報を共有できる仕組みを利用し、認証状態の変化を各タブに通知して、それぞれが最新の状態に追従するようにする。そのために利用するのが、ウェブブラウザに備わる localStorage という機能だ。localStorage は、同じウェブサイトのすべてのタブからアクセスできる共通の保存領域であり、ブラウザを閉じてもデータが消えない特性を持つ。これに対し、sessionStorage はタブを閉じるとデータが消える一時的な保存領域である。
解決策の具体的な手順は次のようになる。
まず、localStorage をタブ間の共有状態を管理する場所として利用する。そして、認証状態(ログインやログアウト)に変化があったときに、localStorage 内の特定のキー、例えば changeInAuth というキーの値を更新する。この更新は、ログイン時にもログアウト時にも行われる。
次に、アプリケーションのすべてのタブで、localStorage の changeInAuth キーに対する変更を常に監視する仕組みを用意する。これはブラウザが提供する storage イベントという機能を使うことで実現できる。storage イベントは、localStorage の内容が他のタブで変更されたときに発生するイベントだ。
もし、あるタブが changeInAuth キーの変更を検知したら、そのタブは自身が持っている認証に関する古いデータ、特に sessionStorage に保存されている無効なデータをすべてクリアする。その後、ページ全体を再読み込み(リロード)する。これにより、リロードされたタブはサーバーから最新の認証トークンやユーザー情報を取得し直し、現在の正しい状態に同期される。
この一連の動作をコードに落とし込むと、以下のようになる。
ステップ1として、アプリケーションの起動時に、storage イベントを監視するリスナーを設定する。JavaScriptの useEffect フックなどを用いて、window.addEventListener('storage', handleLocalStorageChange) というコードを追加する。これは「ブラウザの localStorage に変更があったら、handleLocalStorageChange という関数を実行してね」とブラウザに指示しているのと同じだ。handleLocalStorageChange 関数の中では、event.key というプロパティを使って、どのキーが変更されたのかを確認する。もし event.key が changeInAuth であれば、認証情報が変更されたと判断し、sessionStorage.clear() を実行して、そのタブに保存されている一時的なセッションデータをすべて消去する。その後、window.location.reload() を呼び出して、現在のページを完全に再読み込みさせる。このイベントリスナーは、アプリケーションが終了するときやコンポーネントがアンマウントされるときにきちんと解除する (window.removeEventListener) 必要がある。
ステップ2として、ユーザーがログインする処理の中で、認証が成功し、必要なトークンやユーザー情報が保存された後に、localStorage を更新するコードを追加する。具体的には localStorage.setItem('changeInAuth', Date.now().toString()); を実行する。ここで Date.now().toString() を使って現在のタイムスタンプを保存するのは、毎回異なる値を設定することで、storage イベントを確実に発生させるためだ。同じ値を何度設定しても storage イベントは発生しない。
ステップ3として、ユーザーがログアウトする処理の中でも同様に、ログアウト処理が完了し、トークンやセッションデータがクリアされた後に、localStorage を更新するコードを追加する。ここでも localStorage.setItem('changeInAuth', Date.now().toString()); を実行する。
このように実装することで、システムは次のように機能する。ユーザーがログインまたはログアウトを行うと、その操作を行ったタブが localStorage の changeInAuth キーに新しいタイムスタンプを保存する。この変更は、同じドメインで開かれているすべての他のタブに対して storage イベントとして通知される。storage イベントを受け取った各タブは、認証状態が変わったことを検知し、自身が持っている無効なセッションデータをクリアし、ページをリロードする。リロードされた各タブは、改めてサーバーから最新の認証トークンやユーザーデータを取得するため、常に最新の認証状態に同期される。
このアプローチにより、アプリケーションは複数のタブが開かれていても、常に一貫した認証状態を維持できるようになる。ユーザーが意図しないセッション切れに遭遇したり、ログアウトしたはずなのに他のタブではまだログイン状態に見えたりするような、不便で混乱を招く状況を防ぐことができる。また、単一のユーザーセッションを適切に管理し、ログインやログアウトのたびに、すべての関連タブが自動的に正しいユーザー状態に切り替わるようになるため、ユーザー体験が大きく向上し、アプリケーションのセキュリティと信頼性も高まる。
ブラウザの storage イベントは、このような複数のタブ間で状態を同期させる問題を、わずかなコードで効果的に解決できる非常に便利な機能だ。これにより、開発者はユーザーにとってより快適で安定したウェブアプリケーションを提供できるようになる。