Webアプリパターンの歴史 - SST、AJAX、CSR、SSR、SSG、そしてISR

## はじめに (1/2)

Webアプリの動作するパターンをまとめました。歴史を振り返ることで JAMStack といったモダンなスタックがどういった点で有用なのか理解していきましょう。

発表者：[@kimizuy](https://twitter.com/kimizuy)
日々スプラのXPをどう上げるかに頭を悩ませています。[ブログ](https://blog.kimizuy.dev/)もあります。

---

## はじめに (2/2)

これは2020年9月16日開催の[りあクト！ TypeScriptで始めるつらくないReact開発 第3版Ⅰ. 言語・環境編　読書会](https://qitz.connpass.com/event/189107/)のLT用資料として作成しました。

※ 続きの会も企画する予定です。どちらかというと初学者〜中級者向けです。興味のある方はお気軽にご参加ください。

---

## SST （1/3）

### 概要

- Server Side Templating の略
- 基本的にはサーバサイドのフレームワークとテンプレートエンジンの組み合わせ
- 完成した HTML をクライアントに返すため正確にはサーバサイドレンダリングだが、当時はこの手法しかなかったので後付けでこの呼び方になった
- 主役は Ruby などのサーバーサイドで HTML や JavaScript は脇役だった
- サーバーのレスポンスを受けると画面がクリアされ新しいページがレンダリングされる（都度画面がリセットされる）
- 内容の更新にはリロードなどが必要

---

## SST （2/3）

![gen1st.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/e9cafe13-a081-a76b-3a13-e9f98e8146d3.png)

> 出典：渋川よしき『Real World HTTP ミニ版』図5-6

サーバサイドで取得したデータをテンプレートに流し込んでHTMLを生成し、それをブラウザに返します

---

## SST （3/3）

### 例

- Express × Pug
https://expressjs.com/ja/guide/using-template-engines.html

- ほかにも Rails × ERB や Laravel × Blade など

---

## AJAX （1/2）

### 概要

- Asynchronous JavaScript + XML の略
- IE 製の XMLHttpRequest API が発端
- 次いで Server-sent Event、WebSocket、fetch といった通信APIが規格化される
- フロントエンドの仕事が増大して、JQuery で DOM を操作したり、状態管理のために Backbone.js などのクライアントサイドMVCフレームワークが導入されたりする
- XML ってついてるけどパフォーマンスの観点から JSON が一般的なデータフォーマットとして利用されている

---

## AJAX （2/2）

![gen2nd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/fe9f2b54-52a2-0af9-2755-a04d1c509680.png)

> 出典：渋川よしき『Real World HTTP ミニ版』図5-7

GET や POST メソッドを利用してウェブ API からデータを受け取り、HTML を動的に更新します


---

## CSR （1/3）

### 概要

- Client Side Rendering の略
- SPA (Single Page Application) の始まり
- React、Vue.js、Angular といったライブラリ・フレームワークが開発される
- AJAX 環境よりも充実したライブラリ・エコシステムがある
- コンポーネント指向
  - 単一責任の原則：「ひとつのコンポーネントは理想的にはひとつのことだけをするべきだ」
  - ロジックを分離してカプセル化し保守性を向上
  - コードの再利用性を高める


> 参考：[React の流儀](https://ja.reactjs.org/docs/thinking-in-react.html) 

---

## CSR （2/3）

![gen3rd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/a29d0e13-ac17-e5b6-00ef-33dde7c8dfaa.png)

> 出典：渋川よしき『Real World HTTP ミニ版』図5-7

- すべてのロジック、データフェッチ、テンプレーティングやルーティングがクライアントで行われる
- 最初にほぼ空の HTML を読みこみ、JavaScript から DOM を生成・操作する
- 仮想 DOM 機能（React、Vue）で差分だけを高速で更新が可能
- History API というブラウザ API をラップした Router ライブラリでページ遷移を擬似的に表現でき UX が向上

---

## CSR （3/3）

### デメリット

- アプリが大きくなると JavaScript ソースコードも肥大化し、クライアントで JavaScript を読み込むためクライアントの処理負荷や通信量の増大
- ほぼ白紙の HTML と JavaScript を受け取ってからページを生成するため、すべてのコンテンツが表示されるまでの待機時間（TTI）が長い
- 上記を考慮した遅延ロード処理が必要
- SEOに不向き（* Google クローラは JavaScript を理解するのであまり気にすることはありませんが、SSR や SSG のほうがより予測可能です）

---

## SSR （1/3）

### 概要

- Server Side Rendering の略
- サーバー上でユーザーから"リクエストを受け取ったタイミングで"静的なページを生成して返す
- Next.js や Nuxt.js といったフレームワークがある
- この場合の SSR は厳密には CSR と SSR の組み合わせ

---

## SSR （2/3）

![gen35th.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/d7bd8d51-035a-4774-24b6-6c3cac53edc3.png)

> 出典：渋川よしき『Real World HTTP ミニ版』図5-8

- サーバー側で JavaScript を実行するため、ユーザー側の処理能力に依存しない
- サーバーがリクエストを受けた時点で HTML を生成し、それをブラウザに返す
- 上記以降は CSR で DOM を更新する
- 生成済みの HTML を受け取るため高速なローディングが可能
- CSR よりも SEO 対策に向いている

---

## SSR （3/3）

### デメリット

- リクエストを受けてから HTML を生成し、送信するので初期表示が遅くなる
- フレームワークに頼らない場合は実装が複雑になる↓

> サーバーレンダリングを「正しく」行うためには、コンポーネントのキャッシュ、メモリ消費の管理、メモ化の適用、その他多くの懸念事項に対するソリューションの検索や構築が必要です
> [Rendering on the Web](https://developers.google.com/web/updates/2019/02/rendering-on-the-web?hl=ja#server-vs-static)

---

## SSG （1/3）

### 概要

- Static Site Generation の略
- 厳密にはビルド時に静的なページを生成する機能を指す
- Next.js、Gatsby.js、Hugo、Jekyll といったフレームワークがある
- いわゆる "JAMStack" は SSG を利用してマークダウンファイルや API のデータから事前に HTML を生成して CDN で配信すること

＊ 以降、SSG というより SSG を利用した JAMStack の説明です

---

## SSG （2/3）

![ssg-host-flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/0799c14f-ce1e-04c0-f1b2-8e7359ba9751.png)

> 出典：https://www.netlify.com/blog/2020/04/14/what-is-a-static-site-generator-and-3-ways-to-find-the-best-one/



- ビルド時にあらかじめ静的なファイルを生成する
- また各 URL に対応する個別の HTML ファイルを前もって作成する
- ホスティングサービス （Netlify や Vercel） で生成済みの HTML をホスティングするので運用コストもかかりにくい
- 初期表示時、クライアントは完成済みの HTML を受け取るだけのためパフォーマンスが高い
- パフォーマンスの観点から SEO に強い

---

## SSG （3/3）

### デメリット

- 例えば、ECサイトなどのめちゃくちゃページ数の多いサイトはページの追加や内容の更新時に事前ビルドのコストが大きくなる

> 静的レンダリングの欠点のひとつは、すべての有効なURLに対し、個々にHTMLファイルを生成しなければならないことです。 これらのURLが事前に、また多くの固有ページを持つサイトであるか予測できない場合、これは難題であり実行不可能になることもあります
> [Rendering on the Web](https://developers.google.com/web/updates/2019/02/rendering-on-the-web?hl=ja#static-rendering)

---

## そして ISR へ (1/6)

- Incremental Static Regeneration の略
- 日本語で直訳すると「増分静的再生成」
- ビルド後に静的生成されたページを追加したり、更新したりできる
- Next.js 9.5 より正式な機能としてリリースされた

---

## そして ISR へ （2/6）

![whatisisr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107152/03e26f2d-980f-bdae-896d-6199bee5b666.png)

> https://arunoda.me/blog/what-is-nextjs-issg

図にある Regeneration Process にはページ追加/更新の2通りのパターンがあります（組み合わせも可能）。

---

## そして ISR へ （3/6）

### 前提

- `getStaticProps()`のなかに外部データをフェッチするための処理を書く。ビルドサーバー（Node.js環境）で実行される

- `getStaticPaths()`データに基づいて、プリレンダリングするルート一覧を動的に返す

---

## そして ISR へ （4/6）

### 動的に静的ページを追加

「無数にあるページを事前に生成しておくとビルドが遅くなるし、ページの情報を更新するには CSR や SSR での処理が必要なのでは？」
**→ Incremental Static Regeneration で解決！**

- `fallback: true`：ビルド時にパスが生成されなかったページは、ユーザーの要求のタイミングでページを生成する
- 次のアクセス以降は生成したページを返す


```js
// pages/products/[id].js

export async function getStaticProps({ params }) {
  // ...
}

export async function getStaticPaths() {
  // ...

  // fallback: true means that the missing pages
  // will not 404, and instead can render a fallback.
  return { paths, fallback: true }
}

export default function Product({ product }) {
  const router = useRouter()

  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render product...
}
```

---

## そして ISR へ （5/6）

### 動的に静的ページを更新

「ページの情報を更新するには CSR や SSR での処理が必要なのでは？」
**→ これも Incremental Static Regeneration で解決！**

- 取得したデータに変更があった場合はページを再生成する
- 影響を受けるページのみ更新する

```js

// pages/products/[id].js

export async function getStaticProps({ params }) {
  return {
    props: {
      product: await getProductFromDatabase(params.id)
    },
    // 秒数を指定
    revalidate: 60
  }
}
```

---

## そして ISR へ （6/6）

### デモ

次のふたつは動的に静的なページを追加・更新するデモです

- 無数にあるツイートのうち一つのツイートを静的なページとして追加するデモ

[static-tweet](https://static-tweet.now.sh/)

-  GitHub のリアクションが更新されたら、静的ページに反映するデモ

[reactions-demo](https://reactions-demo.now.sh/)

---

## 参考にした文献やサイト

『Real World HTTP ミニ版』
https://www.oreilly.co.jp/books/9784873118789/
（[Real World HTTP 第2版](https://www.oreilly.co.jp/books/9784873119038/)も！）

React の流儀
https://ja.reactjs.org/docs/thinking-in-react.html

Rendering on the Web - Web上のレンダリング
https://developers.google.com/web/updates/2019/02/rendering-on-the-web?hl=ja#csr

AJAX
https://developer.mozilla.org/ja/docs/Web/Guide/AJAX

Next.js: Server-side Rendering vs. Static Generation
https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation

---

以上です。
ありがとうございました。
[@kimizuy](https://twitter.com/kimizuy)