SSR, CSR, SSG, ISG, ISRの違いと使い分け方。それぞれNext.jsでTodoアプリ作ってみた。
最近初めてNext.jsを触ったのですが、レンダリング方法が色々ありすぎて混乱したので、調べてみました。
似たような3文字の略語多すぎ、、、
WEB+DB PRESS vol.123 でNext.jsのレンダリング方法の使い分け方が特集されていたので、その内容をまとめました。
勉強会の目的はレンダリング方法の違いを理解して、状況によって適切な使い分けができるようになること、です!
目次
SSR, CSR, SSG, ISG, ISR ってそもそもなんじゃ?
それぞれでNext.jsのTodoアプリ作ってみた
実装、使い分け方、メリットデメリット
CSR, SSR, SSG,( ISG, ISR) ってそもそもなんだっけ?
レンダリングを行う場所とタイミングの話です。
サーバー側でレンダリングするか、クライアント側でレンダリングするか。
ユーザーリクエストの前にレンダリングするか、リクエスト後にレンダリングするか。
ページの表示速度やサーバーの負荷が変わるため、状況に合わせて選択する必要があります。
1つの方法が生まれ、その方法の問題点を解決するために次の方法が生まれる。
以下、歴史順で紹介!
昔々、全てはサーバー側で生成されていて、サーバーが神だった。。。
CSR (Client Side Rendering)
JSを使ってブラウザ側でレンダリングする方法です。SPAとかのこと。
クライアントサイド レンダリング(CSR)は JavaScriptを使用し、直接ブラウザでページをレンダリングすることを意味します。すべてのロジック、データフェッチ、テンプレーティングやルーティングは、サーバーではなくクライアント上で扱われます。
CSRの問題点:
クライアント側で処理するJSのサイズが大きくなり、初回表示に時間がかかる。(ユーザーのデバイススペックに依存)
OGP表示など、サーバー側でレンダリングされた値を返せない
SSR (Server Side Rendering)
初回表示はサーバー側でデータ取得やレンダリングを行い、それ以降はクライアント側で内容を更新する。
→僕の担当案件とかコレ
余談:
SSRの呼び方紛らわしい問題。
初回のみサーバーサイドでレンダリングして、それ以降の遷移はフロント側で行う処理一連をまとめて「SSR」と呼ぶ場合と、
サーバーサイドでレンダリングすること自体を「SSR」と呼ばれる場合がある。
→「サーバー側でレンダリング」、「ブラウザ側でレンダリング」と言う方が伝わる、、
SSRの問題点:
重いSPAより初回表示は早くなるが、それでもユーザーからのリクエストを受けてからhtmlを生成するので、時間がかかってしまう。
SSG (Static Site Generation)
静的サイトジェネレーション。
ビルド時に事前にHTMLを構築しておく。この際にAPIでのデータフェッチも済ませておく。
ユーザーリクエストの事前にすべてのhtmlが構築される。
CDNにキャッシュさせることにより、SSRより高速な配信が可能になる。
SSGの問題点:
ページが大量にある場合、ビルドに時間がかかりすぎる。新ページが増えるたびにすべて再ビルドしてられない。
データが更新された場合、再ビルドするまでページの内容が更新されない。
ISG (Incremental Static Generation)
SSGのページが大量にある場合にビルド時間がかかりすぎる問題を解決。
フォールバックと呼ばれる挙動。事前にすべてのページを生成しない。
1. 生成していないページに対してユーザーからのリクエストがあった際、データを取得していない空のHTMLと、JSが返され、ブラウザ側でデータををフェッチしてHTMLを構築する。
2. 1のリクエストの際に、同時にサーバー側でも同様のデータフェッチが行われて、HTMLが構築される。
3. 同じページへの2回目以降のアクセスでは、サーバー側で構築されたHTMLが返却される。
ISR (Incremental Static Regeneration)
SSGの、データが更新された場合に再ビルドするまでページの内容が更新されない問題を解決。
stale-while-revalidateという挙動をする。
ページに有効期限を設け、有効期限が切れたあとの次の1回のリクエストはとりあえず古いキャッシュを返し、裏側でページを再ビルドしておく。
それぞれでNext.jsのTodoアプリ作ってみた
サーバー側はFirebaseのFirestore
Next.jsはページ単位でレンダリングの方法を選べる
余談:
Vercelめちゃ便利ですね!
5分でデプロイの環境つくれる。stg環境もブランチごとに自動的にできる。個人使用無料。
個人でホスティングサービス使うときとかもうこれだけでええやん、、
実装、使い分け方、メリットデメリット
使い分け方フローチャート
(参考: WEB+DB PRESS vol.123 p53 の図↓)
https://gyazo.com/4128b7318a63f1927deb5b277b2f3b19
CSR (Client Side Rendering)
詳細ページ実装: useEffectの中でデータ取得
code:todoId.tsx
useEffect(() => {
const getTodoItem = async () => {
const todo = await getTodo(todoId as string);
setTodo(todo);
setGeneratedAt(now());
};
getTodoItem();
}, []);
table:CSR Pros and Cons
メリット デメリット
HTMLが毎回最新になる SSR, SSGと比較して、ブラウザにコンテンツが表示されるまでが遅い
サーバーのCPU負荷が低い OGP表示など、サーバー側でレンダリングされた値を返せない
SSR (Server Side Rendering)
詳細ページ実装: getServerSidePropsの中でデータ取得
code:todoId.tsx
export const getServerSideProps: GetServerSideProps = async (context) => {
const { params } = context;
const todo = await getTodo(params.todoId as string);
const props: Props = { todo, generatedAt: now() };
return { props };
};
table:SSR Pros and Cons
メリット デメリット
HTMLが毎回最新になる サーバーのCPU負荷ががかなり高い
OGP表示など、サーバー側でレンダリングされた値を返せる サーバーでの処理に時間がかかる
SSG (Static Site Generation)
詳細ページ実装: getStaticPropsの中でデータ取得, getStaticPathsの中でパス指定
code:todoId.tsx
export const getStaticProps: GetStaticProps = async (context) => {
const { params } = context;
const todo = await getTodo(params.todoId as string);
const props: Props = { todo, generatedAt: now() };
return { props };
};
export const getStaticPaths: GetStaticPaths = () => {
// ユーザーからのリクエストより前にtodoIdを知る必要があるため、事前にすべてのtodoIdのパスをリストとして指定する。
// (通常は動的にAPIから取得するが、今回はわかりやすさのためにベタ書きしちゃってます。)
const paths = [
'/todo/ssg/Fkz527Hgh5UCSvJQyaH3',
'/todo/ssg/1tYaCs4QnBBgNjKb80r8',
'/todo/ssg/SjpZXOKpJG5maSQYcDlC',
];
return { paths, fallback: false };
};
table:SSG Pros and Cons
メリット デメリット
HTMLを配信するだけなので、CPU負荷が低い ビルドしないとコンテンツの更新ができない
事前にビルドされてるので速度が爆速 HTMLが最新の情報でない
ISG (Incremental Static Generation)
詳細ページ実装: SSGとほぼ一緒。 違う部分はgetStaticPathsの中でfallback: true指定しているとこ。
code:todoId.tsx
export const getStaticProps: GetStaticProps = async (context) => {
const { params } = context;
const todo = await getTodo(params.todoId as string);
const props: Props = { todo, generatedAt: now() };
return { props };
};
export const getStaticPaths: GetStaticPaths = () => {
// pathsに指定のないものは、ユーザーからのリクエストがあったときに裏側で生成される。
const paths = [];
// faleback: trueの指定でISGの挙動をするようになる。
return { paths, fallback: true };
};
table:ISG Pros and Cons
メリット デメリット
事前に大量ビルドしなくても良い HTMLが最新の情報でない
すべてビルドしなくても、ページを増やせる 一度作成したページ内容は再ビルドするまで更新されない
ISR (Incremental Static Regeneration)
詳細ページ実装: SSGとほぼ一緒。 違う部分はgetStaticPropsの中でrevalidate: 秒数を指定しているとこ。
code:todoId.tsx
export const getStaticProps: GetStaticProps = async (context) => {
const { params } = context;
const todo = await getTodo(params.todoId as string);
const props: Props = { todo, generatedAt: now() };
// revalidate: 秒数の指定でISRの挙動をするようになる。
// 15秒間はキャッシュを返す。15秒後の次のリクエスト時もキャッシュを返すが、そのリクエストをトリガーに裏側でページを再生成しておく。
return { props, revalidate: 15 };
};
export const getStaticPaths: GetStaticPaths = () => {
const paths = [];
return { paths, fallback: true };
};
table:ISR Pros and Cons
メリット デメリット
ビルドしなくてもページの中身の更新が可能 HTMLが最新の情報でない
ISRに対応しているサービスを使用しなければいけない