Web全般

SPA・SSR・SSGについて整理してみた

Web developmentWeb全般

先日コミュニティでSPA・SSR・SSGとは?というLTをする機会がありました。

折角なのでより詳しくして、まとめてみました。

(その時使用したスライドは簡潔すぎてあまり意味がないので、、掲載は控えます)

ブラウザがWebページを表示する仕組みについて

今回の記事では主に、大きく分けてレンダリングをサーバー側 でやるかクライアント側でやるかという違いやメリデメをまとめています。

この「レンダリング」ですが、コードを良い感じにブラウザで表示してくれるようにしてくれること。くらいの理解だったので、まずここをきちんと理解してみました。

レンダリングって奥深くて面白い…!

予想以上に長くなってしまったので、別の記事にまとめました。

レンダリングについてざっと知りたい方はご覧ください。

SPAのおさらい

SPAとは、Single Page Application の略。

別の呼び方で、CSR(Cliant Side Rendering)とも呼ばれてますが、同じです。

クライアント側でレンダリングするフロントエンド技術手法のアーキテクチャ(構造)のこと。

CSR手法を取り入れることで、SPAになります。

ちなみに、ユーザーからするといくつもページがあるように思えるのに、なぜシングルページと呼ぶのかというと、アプリを構成するHTMLが一枚だけなので、そのように呼ばれています。(通常だとページごとにHTMLファイルが存在する)

一枚だけのindex.html

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<App />, document.getElementById('root'));

SPAの特徴

従来との違いを図にまとめてみました。

従来の手法は、MPA(Multiple Page Application)と呼ばれます。(各ページごとにHTMLファイルが存在する為)

  1. ユーザーがリクエストする
  2. サーバー側で全情報を取得してHTMLを生成
  3. HTMLをクライアントに返す
  4. HTMLを描画(ページ表示)

これをページ遷移の度に毎回行う。

SPAの場合はこんな感じです。

  1. ユーザーがリクエストする
  2. 初回だけ、ほぼ空のHTMLファイルと全ファイルをサーバーから返してもらう
  3. ブラウザ側でHTMLを生成して表示
  4. 2回目以降は差分だけ(必要なデータのみ)APIサーバーにリクエストし、返されたデータをブラウザ側で処理(js実行)して表示

図について少し補足します。

Ajaxとは、Asynchronous JavaScript + XMLの略で、JavaScriptを使って非同期通信を行うこと

通常の、同期通信ではサーバにリクエストして、レスポンスが返るまで他の処理は走らず順番に処理がされますが、非同期処理は他の処理が走っている状態で、裏で処理を行える。

(なので、ユーザーが処理を待つことなく画面を常に見る(動かす)ことができる)

JSONとは、JavaScript Object Notationの略で、データ記述言語

Ajaxのサーバー側は、JavaやPHPなどで書かれても、JSONを使うことで言語を超えてデータのやり取りが可能になるということですね。

以下、Wikipediaの引用

JavaScript言語以外でも、ほとんどの言語においてJSONは単純な処理で書き出しや読み込みができる。そのため、JSONは異なるプログラミング言語の間でのデータの受渡しには能率的である。ウェブアプリケーションの場合において、ウェブクライアントでのJavaScriptとのデータの受渡しなどはその最たる活用例と言える。

JavaScript Object Notation

ちなみにAjaxの最後の単語「XML」。これはJSONと同じようにテキストベースのデータ記述言語。当初はXMLを使ってサーバーからのデータ取得をしていましたが、JSONが誕生してこちらの方が記述が簡潔なので、今ではJSONが主流の傾向です。

(調べてみるとわかりますが、XMLの記述方法は確かに冗長でわかり辛い…)

もう一つ、このAPIサーバーとは、従来のWebサーバーと何が違うのか。

そちらのお話はこちらのサイトを参考にして理解を整理してみました。

WebとAPIとサーバレスと ~アーキテクトコラム~

以前は、ブラウザは描写だけの担当だったので、Webシステム側でUI、ロジック操作、データ構築・提供まで全てを描写していました。が、一体化だと修正の範囲が多くなり複雑化しやすいという問題が。

そこで生まれたのが、3層アーキテクチャ(サーバを3層に分けること)。

  • プレゼンテーション → UIに関連する層。画面のデザイン、レイアウトを生成したり。
  • アプリケーション(ビジネスロジック) → プレゼンテーションから受け取った情報で何かしら処理をする層。
  • データ → あらゆる情報を持っているデータベース

MVC(Model View Controller)と似ている?と勘違いしましたが、MVCはUIを持つアプリケーションのデザイン方法なのでそもそも違いました。上記の3層構造に当てはめると、プレゼンテーション側のみの話(Modelはアプリケーションに該当)。

しかし、Webはもっと複雑になっていき動的な処理が必要になっていきます。ECサイトの例では、どのユーザーが商品カゴに何を入れたか、など。

動的な処理方法として2つあります。一つは、アプリケーション層で処理をするという単純な方法で、Web/APサーバ一体型が主流でした。しかし、プレゼンテーションとアプリケーション側の境界が不明瞭になっていきます。もう一つの方法は、「プレゼンテーション側で制御をする」というやり方。アプリケーション側に、プレゼンテーション側が必要な要素や処理をリクエストして、アプリケーションは必要な処理のみ提供します

→アプリケーション側でAPIを提供するという構図が出来上がったのです。(WebAPI)

注目されるようになった背景

JavaScriptの進化

一昔前までは、JavaScriptはページを部分的に動かす程度でしか使われておらず、また処理速度も遅かったのです。が、2008年にGoogle Chromeが登場し、搭載されていたv8というエンジンがJavaScriptを高速に処理することを可能にしました。

スマートフォンの普及

当時、少しリッチな表現をする時(動画など)は、Adobe Flashが使われていましたが、スマフォには重すぎて不向きでした。

また、Steve JobsはAdobe Flashに対してとても批判的で、需要があったのにも関わらずiosではサポートしないという文章(Thoughts on Flash)を2010年に公開。

Flashを脱却してHTML5とJavaScriptでリッチなアプリケーションを作ることを推進し、Adobe Flashは衰退していきます。

jQueryでSPAを開発することも可能でしたが、直接DOMをいじるので大規模なSPAを作るのは限界がありました。

そして、MVCやMVVMのようなUIを分離する設計思想を取り入れたフレームワークが登場していき、大規模なSPA開発が可能になったのです。

3大フレームワーク

Angular(2010年/Google)、React(2013年/Facebook)、Vue(2014年/EvanYou)

SPAのメリット

  • ページ遷移が高速
  • サーバーとの通信を最小限にすることで優れたUI/UXを提供
  • 構成がシンプル(最低限HTMLとJavaScriptファイルのみでデプロイ可能)

構成がシンプルというところは、GithubやFirebase、Netlifyなどでのホスティングサーバーを使えば簡単にデプロイできちゃいますね。

適用している企業例

  • Facebook
  • Twitter
  • Airbnb
  • Netflix

などなど。

SPAのデメリット

そんな高速で最高なSPAにもデメリットがあります。

初期表示が遅い

初回にユーザーからリクエストがあった時はサーバーから全コンテンツが返ってきます。それをブラウザ側で組み立てるので、初期表示に時間がかかってしまう。JavaScriptのファイルが多い程、処理時間がかかりユーザーは真っ白な画面を見ることになります。

また、ブラウザ側でレンダリングをするので、ユーザーの使用端末環境に依存してしまいます。(ポンコツ携帯の場合は処理速度が遅くなる)

SEOに弱い可能性がある

初回にサーバーから返されるHTMLはほぼ空のファイルなので、Googleクローラーがコンテンツを認識できないという話。ただこの問題は、現在はJavaScript実行後に認識してくるので改善されつつあるらしいです。

しかし100%保証されているわけではないそうで、タイムアウトの問題があったりします。

クローラー さんもいつまでも待ってくれないので、JavaScriptの処理が遅いとタイムアウトしてインデックスされない可能性があるとのこと。

どれくらいでタイムアウトしてしまうのかという検証を説明している記事がありました。

State of SEO for SPA 2019

基本的には数秒そうですね。

ただし、Paintingまでにエラーが発生しないことが重要だそうです。

また、Vueの公式サイトにはこんなことが書いてあります。

現在のところ、Google と Bing は同期的 JavaScript アプリケーションのインデックスを作成できます。同期がキーワードです。あなたのアプリケーションが読み込み中にスピナが表示され、Ajax 経由でコンテンツを取得する場合、クローラーはあなたが完了するまで待たないでしょう。つまり、SEO が重要なページで非同期にコンテンツを取得する場合は、SSR が必要な場合があります。

#Vue.js サーバサイドレンダリングガイド

なので、やはり時間が重要で、非同期の場合(取得に時間かかるコンテンツ)はGoogleクローラーさんは待ってくれないということですね。

※クローラーとは、インターネット上に存在するWebサイトや画像などあらゆる情報を取得し検索データベースを作成する巡回プログラム。

※インデックスとは、ページがクローラーに登録されること。

動的なOGPに対応できない

TwitterやFacebook等のSNSでシェアをする時に重要になるOGP。

OGPの設定はサーバーから返されるHTMLのメタタグ に書く必要がある為、プレーンなSPAはHTMLのやり取りをしないので、ページごとにOGPを分けるという対応できません。

※「プレーンな」と主張しているのは、例えばReactの場合react-helmetを使えば、何とか動的にOGP設定できたりするからです。

SSRとは

SSRとはServer Side Renderingの略。

SPAの、初期表示に時間がかかる、SEO対策が完全じゃないデメリットを改善するため、サーバーサイド でレンダリング→HTMLを生成して、クライアント(ブラウザ側)では描写(Paintingの部分)だけ対応するやり方です。

サーバーサイドでレンダリングと聞くと、「あれ、従来のMPAに戻るの?」と疑問に思いますよね(私は思いました)。

「サーバーサイド でレンダリングする」という意味は色々ありますが、ここでお話しする「SSR」は、従来のやり方(MPA)とは異なります。

ここで意味するSSR(よくSPAやSSGと比較される昨今のSSR)は、初回のリクエスト時だけサーバーサイド でレンダリングして2回目以降はSPAと同様の動きを可能にします。

(従来のMPAはサーバーサイドをPHPやJava言語で書き、リクエストがある度にHTMLを生成して毎回返すというやり方をするという話でしたね)

そうすることで、SPAとSSRの両方のいいところ取りになり、ダイナミックなアプリが作れるということです。

この新しい形のアプリはuniversal app又は、isomorphic appと呼ぶそうです。

こちらを実現させるには、サーバー側とクライアント側で共通のロジックを書き、サーバー側でJavaScriptが実行できる環境が必要です。その為、Node.jsを使う必要があります。

流れを図にして整理するとこんな感じ↓

SSRの書き方の例はこんな感じ↓

const render = (req, res) => {
  fs.readFile('./index.html', 'utf8', (err, data) => {
    const html = ReactDOMServer.renderToString(<App />);
    const document = data.replace(`<div id="root">${html}</div>`);
    res.end(document);
  });
}

const app = express();
app.et('*', render);

ExpressでReactDomServerモジュールを使う。

クライアントで描写済みのHTMLコンテナを再利用する場合はhydrateを使う。(※レンダー内容はサーバーとフロントで一致させないとバグになる)

import React from 'react';
import ReactDom from 'react-dom';

ReactDOM.hydrate(<App />, document.getElementById('root'));

参考動画:Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19)

(Express(Node.js)まだ全然よくわかっていません。勉強します、はい)

CSRとSSRの動きの違いについて、こちらの動画がとても分かり易いのでご興味あれば是非チェックしてみてください。

#004 Server Side Renderingの動作を確認する(Next.js&TypeScript体験シリーズ)

※動画はNext.jsを使っています。(手軽にSSRが実装可能なReactのフレームワーク)

SSRのメリットを整理すると、

  • SEO対策になる
  • ユーザーの環境に左右されない(サーバー側でレンダリングするので、ポンコツ携帯やネットワークでも大丈夫)
  • 動的なOGPに対応可能(必要であればサーバー側で別のHTML&メタをレンダリングすれば良いので)
  • First View Performance の改善

ですね。

最後のFirst View Performanceについて補足をすると、初期表示のパフォーマンスにも段階がありますが、SSRではFCPからTTLまでの時間が短縮されます

FCP?TTI?何それ…

FCPとは、First Contentful Paintの略で、初めてコンテンツが表示される状態のこと

TTIとは、Time To Interactiveの略で、ユーザーが実際に操作できる状態のこと

page_loading_key_moment.png
引用:Web – Timeline of a page load (Page Speed|Page Latency)

サーバー側でレンダリングすることで、大量のJSファイルをブラウザ側に送る必要がなくなるので(送るのはテキストとリンクだけ)、TTIまでの時間が短くすることができるということですね。

Image for post
引用:Rendering on the Web – Web上のレンダリング

CSRでは、FCPとTTIの時間が長い傾向です。特にJavaScriptファイル が大きくなると顕著。

Diagram showing client-side rendering affecting FCP and TTI
引用:Rendering on the Web – Web上のレンダリング

どちらか良いかという話ですが、「コンテンツが見えてるけど操作できない」= 「FCPとTTIが長い」の方がユーザーはストレスを感じ易いそう。

では常にSSRにしておけば良いのかというと、そんなこともありません。

まず、上の続きで言うと、SSRの場合はTTFB(Time to First Bytes)が遅くなるという欠点があります。

その他、Node.jsを動かすサーバー環境が必要なので実装コストが上がります。(普通に書いたらフルスタックの領域ですよね)

サーバーとフロントの切り分けを考えたり、CSRに比べて複雑さが増します。

また、アクセス数が大量になるとサーバー側の負担が大きくなり、サーバーを動かすのでサーバーの管理もし続けなくてはいけません。

(Next.jsやNuxt.jsと言った手軽にSSRを実装できるフレームワークもありますが、それでも複雑さはCSRに比べて上がる)

SSGとは

そこで出てきたのが、SSGです。

SSGとは、Static Site Generator(静的サイトジェネレーター)の略。

SSGは、HTMLファイル生成をビルド時に事前に行います。レンダリング済みのHTMLファイルをサーバーにホストしておいて、リクエストがあったタイミングで生成されていたHTMLファイルを返すだけになります。

ちなみに「ビルドとコンパイルの違い」や「ビルドがしていること」について、いまいち理解していないなという場合はこちらのサイトが分かり易いのでご参照ください。

「ビルド」機能とは?仕組みやコンパイルとの違いを解説

その為、SSRのように複雑なサーバー処理は必要ないです。

このビルド時に生成する方法で、Pre-rendering(プリレンダリング)静的レンダリングという言葉がありますが、両者は異なります。

プリレンダリングは、クライアント側でJSの実行をすることで使えるページになる。

静的レンダリングは、JSはほぼ不要である。(本当の静的サイト)

この違いを確かめるには、DevツールでJavaScriptを無効にしても表示されるかで分かります。

参考:静的レンダリング

なので、SPAのように(ユーザーごとにデータが異なるような)動作をするアプリを作るのは、プリレンダリングになります(JSの実行が必要な為)。

SSGの流れを図にするとこんな感じ↓

SSGは静的なHTMLファイルを返すだけなので、非常に高速です。

事前にHTMLファイルを生成するので、SEOも問題ありません。OGPもページごとに対応可能ですね。SSRのような複雑さもない。

ただ、この手法が向いてない場合がいくつかあります。

  • リアルタイムに更新が必要なコンテンツ(ビルド時にHTMLファイルを作るので、データの更新は再ビルドした時だけの為)
  • ページ数が大量(ビルド時間も長くなる)

規模が小さいアプリケーションには最適そうですね。

まとめ

SPA・SSR・SSGの違いについてまとめましたが、どの手法を使うのか(あるいは組み合わせたりする)は、ケースバイケースになるということですね。

ユーザーの滞在時間が長く初期表示やSEOはそこまで気にしない → SPA

SEO大事、規模が大きいプロジェクト → SSR

SEO大事、リアルタイムにデータ更新が必要でない → SSG

などなど。

SPA(CSR)だけで終わらせるのは充分でない場合、SSR(universal app)が最強のように思えるけどコストが高い…、なので中間のポジションであるSSGはかなり注目されているようです。

Next.jsなんかも今はSSGを推していて、ISRなんていう新しい手法(時間指定でページ再生成も可能みたいな)も出てます。

何に重きを置くか、プロジェクトの大きさなどで決めていくのが一番だと思いますが、まずはそういう最適な選択をできるような技術レベルを目指したいですね。

とりあえず、Next.jsちゃんと使ったことないのですが(去年触ったけど忘れた)、かなり最強そうなので使ってみたいと思います。使ったら多分ブログ書きます。

参考にしたサイト