📝

Salesforce LWCでGoogle Maps APIを使おうとして詰まった話

に公開

SalesforceのLightning Web Components (LWC) にGoogle Mapsを統合するということを、VisualforceでできるんだからLWCでもできるだろうという軽いノリのではじめたところ、大沼にはまったので記録に残す。

1. Maps JavaScript APIの仕様と「CORSの壁」

まず理解すべきは、Google Maps APIの動的な性質です。

連鎖的なスクリプト読み込み
公式ドキュメントを参考にしながら実装を進めるときに私たちが最初に読み込むGoogle Maps APIのURLは、巨大なライブラリのほんの入り口に過ぎません。この「ブートストラップスクリプト」は、実行されると、地図の表示に必要な追加モジュールを動的にWebページへ追加しようとします。

ブラウザのセキュリティ: この方法は、ブラウザの同一生成元ポリシーの対象となり、リクエスト先のサーバーから許可(Access-Control-Allow-Originヘッダー)がなければブロックされます。Googleのサーバーはこの許可を返さないため、CORSエラーとなります。

これを回避する方法は、<script>タグを動的に生成してDOMに追加することです。<script>タグによる読み込みは**「コードの実行」**を目的としており、歴史的な経緯からCORSポリシーの例外となっているため、この段階ではCORSエラーを回避できます。

2. Salesforceのセキュリティサンドボックス:LockerとLWS

次に理解すべきは、LWCが動作する特殊な環境です。Salesforceはコンポーネントの安全性を保つため、2つのセキュリティアーキテクチャを用意しています。

Locker Service ⛓️
古い世代のセキュリティモデルです。windowやdocumentといったブラウザの標準オブジェクトを、安全なラッパーオブジェクト(SecureWindowなど)で包み込みます。これにより、外部ライブラリが期待する標準オブジェクトと異なる挙動を示し、互換性の問題を引き起こすことがよくありました。

Lightning Web Security (LWS) 🛡️
新しい世代のセキュリティモデルです。オブジェクトをラップするのではなく、JavaScriptのサンドボックス技術(Distortion)を利用して、より標準に近い環境を提供します。これにより、外部ライブラリとの互換性が大幅に向上しています。

3. ローダーの仕様とLockerの仕様の衝突

<script>タグでAPIを読み込む方式に切り替えても、Locker Serviceが有効な環境では、また別のエラーに遭遇します。
**このあたりからのエラー事象の分析は、Geminiの受け売りですし、Google Map Apiのモジュールをロードする内部スクリプトの挙動は一切理解していないので、原因も推測の域です。

現象: Cannot use 'in' operator to search for 'Symbol' in undefinedのようなエラーが発生。

原因: Google Maps APIのスクリプトが、自身の初期化処理中に、モダンなJavaScriptの機能(Symbolなど)がブラウザに存在するかをチェックしようとします。しかし、Locker Serviceのラッパーオブジェクトが原因で、スクリプトが期待するオブジェクトが見つからず、undefinedなものに対して操作しようとしてエラーが発生します。

4. ローダーの仕様とLWSの仕様の衝突(根本原因)

LWSを有効にするとLockerとの互換性問題は解決しますが、さらに根深い問題が姿を現します。

現象: 最初のCORSエラーが形を変えて再び発生する。コンソールにはCannot access nonceというエラーも記録される。

原因(エラーの連鎖):

私たちのローダーが、Google Mapsのブートストラップスクリプトを正常に読み込む。

ブートストラップスクリプトは、追加モジュールを読み込むために、動的に新しい<script>タグを生成しようとする。

Salesforceの厳格なContent Security Policy (CSP) に準拠するため、この新しい<script>タグにはnonce(ワンタイムトークン)を付与する必要がある。

Googleのスクリプトは、ページに存在するnonceを取得して、新しいタグに設定しようとする。

しかし、LWSのサンドボックスは、セキュリティ上の理由から、内部で実行されるスクリプトがこのnonceプロパティにアクセスすることをブロックする。

nonceの設定に失敗したGoogleスクリプトは、代替手段としてfetchによるモジュール読み込みを試みる。(ここがよくわからない。GoogleのサーバーはCORS対応していないのに、なぜCORSありきな手段を代替として用意している?)

このfetchリクエストが、最初の問題であったCORSエラーによってブロックされる。

結論:iframeによる環境の分離が唯一の安定解

Google Maps APIのように、それ自体が複雑なアプリケーションとして、自身の依存関係を動的に解決しようとするライブラリは、LWSのセキュリティサンドボックスと根本的に相性が悪いことがわかります。

この問題に対する最も確実で、Salesforceが推奨する解決策は、Visualforceページをiframeで埋め込むことです。

iframeは、LWCとは完全に隔離された別のブラウジングコンテキストを提供します。iframe内のVisualforceページはLWSの制約を受けないため、Google Maps APIはその中で何にも妨げられることなく、自由にスクリプトを読み込み、地図を描画できます。LWCとiframe間の通信にはpostMessageを利用することで、安全にデータを連携させることが可能です。

この方法は一手間増えますが、外部ライブラリとLWCの間に明確な境界線を引くことで、将来のアップデートにも強い、最も堅牢なアーキテクチャとなります。

Discussion