Invisible Pages
高負荷サイトを設計するとき、SPAの誘惑は強い。
ページ遷移のたびにサーバーへリクエストを飛ばさなくていい。JavaScriptがDOMを書き換えるだけだから、HTMLレンダリングの負荷が消える。REST APIでデータだけ返せばいい。月間数千万PVのサービスで、この差は大きい。
だがSPAには影がある。
GooglebotはChromiumベースのレンダラーを持っている。だがクロールとレンダリングの間に遅延がある。非同期でデータを取得するタイミングが合わなければ、空のページとして記録される。ユーザーには見えているページが、検索エンジンには見えていない。大規模サイトほど検索流入への依存度が高い。SPAが得意なこととSEOが求めることが、真正面からぶつかる。
もうひとつ気になるのは、REST APIが外から見えることだ。SPAはAPIを叩いてデータを取得する。そのAPIは、ブラウザのDevToolsを開けば丸見えになる。認証やレート制限で守るとしても、エンドポイントの構造もパラメータも隠せない。意図しない形でデータを抜かれるリスクは常にある。SSRならデータをHTMLに埋め込んで返すだけだからAPIを外部に露出する必要がない。もちろんSSRでもスクレイピングはされる。だがHTMLをパースするのと、構造化されたJSONを叩くのでは、攻撃者の手間が違う。
妥協点はある。初回アクセスだけSSRで返し、以降はSPAとして動くハイブリッド方式。Next.jsやNuxtのアプローチだ。だがハイブリッドにはハイブリッドの複雑さがある。サーバーとクライアントで状態を同期し、ハイドレーションの不整合というまた別の種類のバグと向き合うことになる。
そしてREST APIを外に出すと決めた瞬間、次の沼が口を開ける。認証をどうする。OAuth、OIDC、トークンの管理。APIが増えればサービスを分割したくなる。分割すればマイクロサービスの設計が必要になる。サービス間の認証、データの整合性、障害の伝播。SPAという選択は、フロントエンドのアーキテクチャの話に見えて、バックエンド全体の設計を引きずり込む招待状だ。
結局、何を捨てるかを選んでいるだけだ。SPAを選べばSEOとセキュリティの表面積が広がり、その先にマイクロサービスの沼が待っている。SSRを選べばスケーラビリティと戦うことになる。キャッシュ戦略で緩和はできるし、構成はSPAより単純になる。ハイブリッドを選べば両方の複雑さを引き受ける。
どれを選んでも、どこかで殴られる。せめて殴られる場所は自分で選びたい。