エンジニアを目指す初学者に向けて、わかりやすく解説したブログです。

SPAのここがダメ(特に初心者はやってしまいがち)

Webサービスの実装方法として、SPA(Single Page Application)という選択肢がある。

SPA実装上の大きなデメリットを明確に書いていない記事ばかりなので、ここに書く。

結論

  1. 自分がSPAで実装したWebサービスについて、全てのページの組み合わせでブラウザバックをテストしていますか?
  2. SPAを使わなければ達成できないビジネス上のメリットを明確に説明できますか?

この2つの問いに答えられないようであれば、SPAで実装すべきではない

特にVueやReactを学びたての初心者は意識せずにSPA実装してしまう(CSRとSSRの区別がついていない)ケースがあるので特に注意が必要だ。

SPAで実装すると発生する不都合5つ

①より多くのデータ取得状態を考慮する必要がある

SSRをする場合、実装時に考慮すべき状態

  1. データ取得に成功
  2. データ取得に失敗
Image in a image block

CSRをする場合、実装時に考慮すべき状態

  1. データ取得前
  2. データ取得中
  3. データ取得に成功
  4. データ取得に失敗
Image in a image block

クライアントサイドでデータを取得し、その情報を使ってDOMを構成する場合
サーバーサイドで処理するときと比較して「データを取得する前」と「データ取得中」の状態を考慮する必要がある。

これが何を意味するかと言うと、

  • ユーザー体験向上の為、ローディングのようなUIが新たに必要になる
  • 成功でも失敗でもない、第三の状態のハンドリングが必要=常にnullやundefinedと戦う必要がある

というように、考えることと実装すべきことが必然的に増えてしまう。

②GA4のようなアナリティクスツールの導入コストが上がる

ページ遷移時にSSRが行われるようなサイトでは、アナリティクスツールのscriptタグを1行埋め込めばPV計測はほぼ自動で行ってくれる。
(スクリプトが読み込まれたタイミングでPVログが発行する仕組みのため)

SPAでは、ページ遷移時にアナリティクスツールの読み込みが発生しないため、PV計測が自動で行われない。
ここはページ遷移と同時に、PVログを送信する実装をあえて行う必要がある。

SSRにしていれば書かなくて良いコードをわざわざ書かなければいけないし、書き漏れのリスクも当然あるだろう。

③ブラウザの進む、戻るなどのテストが念入りに必要になる

SSRで実装している場合、ブラウザの履歴管理は基本的にブラウザ任せでOKとなる。

ただし、SPAを実装している場合は historyを使ったり、 router.pushを使ったりして自前で管理が必要である。

これは開発者が「履歴が変わったときの状態」を自分で合わせる必要があり、ここを意識しないと「進む」「戻る」の挙動で表示がおかしくなることがある。

■例:ページレンダリング初回のみデータを取得する実装をしている場合

データ取得方法の実装によっては、「戻ったときにデータがフェッチできず、データがない状態で表示されてしまう」ということが発生しうる。

Image in a image block

SSRの実装の場合、ページの表示は基本的に冪等性が保たれるため、前のページの状態を意識する必要がなかった。

ただしSPAの動きの場合、ページ遷移の前後で「残り続けるデータ」と「新たに取得するデータ」が部分的にそれぞれ発生するため
遷移の前後でどんなデータが残り続けて、どんなデータが新たに取得されるのかをいちいち考えながら実装することを迫られる。

④APIが公開されているので、攻撃されるリスクが高まる

SPAで実装されているということは、
データ取得のためのAPIがインターネットに公開されていることであり、守るべきものが増える。

SSRの場合、APIを公開せずに設計可能

万が一APIのバリデーションが甘かったとしても、大きなセキュリティリスクになりにくい。

Image in a image block

SPAの場合、APIを公開する必要がある

万が一APIのバリデーションが甘かった場合、大きな事故に繋がる可能性が高い

Image in a image block

⑤機密情報の漏洩リスクが大きい/漏洩しないために努力を要する

例えば、OpenAIやClaudeなどのAPIを使うケースを考えてほしい。

これらの大規模言語モデルのAPIを使うときは、「APIキー」というものを発行する。(このキーがあればAPIにリクエストできる)

SPAは構造上APIへのリクエストが丸裸であるため、SSRの実装よりも情報を隠蔽する努力が必要になる。
(フロントエンドサーバーに安全なプロキシAPIを立てるなど)

SSRの場合

サーバーサイドでOpenAI APIへのリクエスト処理を実行することで、クライアントにAPIキーが漏洩することはない。

Image in a image block

SPAでフロントエンドで実行されるJavaScriptから直接APIリクエストを行っている場合

OpenAI APIにリクエストする処理はブラウザ側で実行されるので、利用者はAPIキーを覗き見ることができる。

「気づいたら悪意のある人が勝手にAPIキーを使っていて、とんでもない利用料を請求された」ということが発生しうる。

Image in a image block

まとめ

  • これらの不都合を上回るほどのビジネス上のメリットを明確にできない限り、SPAは使わない方が良い
    • 基本的にはSSRで実装を行い、必要に応じてCSRの実装を入れるという考え方が良い
  • 初学者が意識せずに実装すると、これらの不都合を抱えたコードを書いてしまう可能性が高いので注意
    • 多くの現場で使われているNext.jsやNuxt.jsはCSRとSSRの境界線が曖昧になっているため、この問題が発生しやすい
  • 特に④と⑤に関しては「知らなかった」で済まされなくなるケースにつながるため、しっかりと理解しておくこと