LoginSignup
15
1

【Next.js】SSRの挙動について理解する(SSRの仕組み、CSRとの違い、getServerSideProps関数)

Last updated at Posted at 2023-07-09

この記事は個人学習の備忘録です。

Udemyの講座【2023年最新】React(v18)完全入門ガイド|Hooks、Next.js、Redux、TypeScript(CodeMafia)

上記を参考・引用しながらNext.jsの基本のレンダリングについて個人的に学んだことのアウトプット記事です。

前回の記事ではCSR/SSR/SSG/ISRそれぞれの基本のレンダリングについてのアウトプットの記事を書きました。

今回のアウトプットではSSRに絞って理解したことをまとめました。

SSRの挙動について理解する

SSRの観点からどのようなタイミングで下記の関数コンポーネントが実行されるのかを理解します。

/src/pages/010_SSR/index.js
export default function SSR() {
  console.log("hello");
}

「010_SSR」フォルダのindex.js内でconsole.logを実行し、ブラウザではこのURLに遷移した状態で更新ボタンを押します。

VSCodeのターミナル上に出ているのがNode.jsの実行ログになります。
スクリーンショット 2023-07-09 14.02.56.png
Node.jsの実行ログにも、ブラウザ上のコンソールにも両方"hello"と出ています。
スクリーンショット 2023-07-09 13.54.48.png
Next.jsの場合では、Node.js上とブラウザ上、どちらもこの関数コンポーネントが実行されていることが分かります。

変数の場合

コードを追加します。

/src/pages/010_SSR/index.js
export default function SSR(){
  console.log("hello")
  // 追加
  const val = 0
  return <h3>{val}</h3>
}

スクリーンショット 2023-07-09 14.12.15.png
上記の場合でもブラウザ上、Node.js上でも"hello"と出てきますが、この0という値はブラウザ上、Node.js上どちらで保管された値なのかをみていきます。

右クリックで検証ツールを開き、「ページのソースを表示」をクリックします。

スクリーンショット 2023-07-09 14.13.58.png

これによってHTMLが表示されます。このHTMLはサーバーから受け取ったHTMLになります。
スクリーンショット 2023-07-09 14.15.29.png
「h3」タグで検索すると、<h3>0</h3>がHTMLの中に存在しています。
スクリーンショット 2023-07-09 14.18.39.png
つまり、Node.js上でvalの値が0に置換されて、ブラウザ側に返却されているということになります。valの値の0というのはNode.js上で保管されていました。これがSSRの機能です。

SSRの場合には、リクエストが送信される(=ブラウザで指定されたURL)と、Node.jsのサーバーに対してリクエストが送信されて、そのリクエストを受け取ると関数が実行されます。
関数が実行されると、変数の部分が代入されて、jsxが返されるので、このjsxがReactの要素となってHTMLに保管されてからブラウザにレスポンスとして返却されます。HTMLがすでに出来上がった状態でブラウザに返却されることになります。

stateの場合

stateの場合はどうなるのかみてみます。

/src/pages/010_SSR/index.js
import { useState } from "react";

export default function SSR() {
  console.log("hello");
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

この時も、値が代入された状態でHTMLが返却されていることが分かります。
スクリーンショット 2023-07-09 14.48.33.png
スクリーンショット 2023-07-09 14.48.49.png

このようにNext.jsの場合ではstateの場合でも値が代入された状態でHTMLが返ります。
Reactは内部に仮想DOMを保持しているため、擬似的にNode.js上でDOMツリーのようなものを作成し、それを元にしてHTMLを作成できるため、Node.js上で値が代入された状態で返却することが可能になっています。

注意点

関数はNode.js上でも実行されるので、Node.jsで存在しないwindowオブジェクトは使用することができません
なので、例えばブラウザでしか保持していないwindow.documentにはアクセスすることはできません。

もし、windowオブジェクトにアクセスするような処理を書きたい場合には、useEffectの中で行います。
useEffectの中に書かれた処理は、必ずブラウザ上で処理されることになります。なので関数コンポーネントのトップレベルには書かずに、useEffectのコールバック関数内に記述することでエラーが出ずwindowオブジェクトにアクセスすることが可能になります。

/src/pages/010_SSR/index.js
import { useState, useEffect } from "react";

export default function SSR() {
  console.log("hello");
  // 追加
  useEffect(() => {
    window.localStorage.setItem("key", "value");
  }, []);
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

この場合ではエラーが出ず実行されますが、window.localStorage.setItem("key","value")をuseEffectから外に出すとエラーが出ます。

/src/pages/010_SSR/index.js
import { useState, useEffect } from "react";

export default function SSR() {
  console.log("hello");
  // 外に出す
  window.localStorage.setItem("key", "value");
  useEffect(() => {
    window.localStorage.setItem("key", "value");
  }, []);
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

スクリーンショット 2023-07-09 15.23.14.png
スクリーンショット 2023-07-09 15.23.29.png

window.localStorageは副作用(※)の処理ということになってきますので、useEffectの処理に含めることになります。

※副作用・・・描画の一部ではない処理のこと
useEffectについての記事はこちら

画面遷移する場合

トップページに戻り、一度画面を更新してから/010_SSRのURLに移動します。
その時、ブラウザのconsole.logには"hello"と追加されましたが、Node.js上のログには何も追加されません。
ezgif.com-video-to-gif (7).gif
スクリーンショット 2023-07-09 15.34.17.png

一方で、最初から/010_SSRで更新すると、ブラウザ上もNode.js上でも"hello"という文字列が追加されます。
スクリーンショット 2023-07-09 15.38.12.png
スクリーンショット 2023-07-09 15.38.32.png
これは、画面が一番最初に表示される時、その時はNode.js上の関数コンポーネントが実行されるのですが、他のページから移ってきた時は関数コンポーネントが実行されません。
あくまで、他のページから移った際はブラウザ上で関数コンポーネントが実行されることになります。

このように、ブラウザ上でレンダリングの処理が実行されることをCSRと呼びます。初期表示の時はSSRで、画面遷移を行う時はCSRになってきます。

getServerSideProps関数について学ぶ

SSRの関数の時に実行されるgetServerSidePropsの関数の使い方について学びます。

/src/pages/010_SSR/index.js
import { useState, useEffect } from "react";

export default function SSR() {
  console.log("hello");
  useEffect(() => {
    window.localStorage.setItem("key", "value");
  }, []);
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

// 追加
export async function getServerSideProps(context){

}

この関数はNode.js上で実行される関数になりますが、この関数のcontextの引数に対して、リクエストとレスポンスの情報が乗ってきます。
returnの値に関しては、サーバーで実行するような値をこちらで実行することができます。

SSR関数コンポーネントの引数にpropsとして渡す値をここで設定することができます。
例えば,propsにmessageというキーを用意し、メッセージの値をSSRの関数コンポーネントの引数に渡すことができます。

/src/pages/010_SSR/index.js
import { useState, useEffect } from "react";

// 引数にmessageを追加
export default function SSR({ message }) {
  console.log("hello");
  // 追加
  console.log(message)

  useEffect(() => {
    window.localStorage.setItem("key", "value");
  }, []);
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

export async function getServerSideProps(context) {
  // 追加
  return {
    props: { message: "From Server Side" },
  };
}

このメッセージがコンソールに出力されます。
スクリーンショット 2023-07-09 16.06.14.png
スクリーンショット 2023-07-09 16.06.31.png

リダイレクトの処理も可能です。010_SSRページに飛んできたものをTOPページに対して転送することになります。

/src/pages/010_SSR/index.js
export async function getServerSideProps(context) {
  return {
    // 追記
    redirect: {
      destination: "/",
    },
    props: { message: "From Server Side" },
  };
}

ezgif.com-video-to-gif (8).gif

この時にパーマネントというオプションを渡すこともできます。パーマネントはリダイレクト処理を一時的に行うか、恒久的に行うかという設定で、trueにすると恒久的にリダイレクト処理を行います。

/src/pages/010_SSR/index.js
export async function getServerSideProps(context) {
  return {
    redirect: {
      destination: "/",
      // 追記
      permanent: true,
    },
    props: { message: "From Server Side" },
  };
}

contextに関しては、リクエストやレスポンス、またはクエリ、パラメーターなどの通信の情報が乗ってきます。
例えばリクエストに乗ってきたcookieを取り出すこともできます。(cookieの値は仮で設定)

/src/pages/010_SSR/index.js
import { useState, useEffect } from "react";

export default function SSR({ message }) {
  console.log("hello");

  console.log(message);

  useEffect(() => {
    window.localStorage.setItem("key", "value");
    // cookieの値を設定
    document.cookie = "val=0; path=/;";

  }, []);
  const [state, setState] = useState("bye");
  return <h3>{state}</h3>;
}

export async function getServerSideProps(context) {

  // 追加
  const { cookie } = context.req.header;
  console.log(cookie);

  return {
    // redirect: {
    //   destination: "/",
    //   permanent: true,
    // },
    props: { message: "From Server Side" },
  };
}

上記のようにcookieの値を設定し、パスをルートパスに指定し、画面更新すると検証ツールのApplicationのcookieの値が設定されてることを確認できました。
スクリーンショット 2023-07-09 23.17.45.png
Node.js上でもval=0が設定されていることが確認できます。
スクリーンショット 2023-07-09 23.21.13.png
このように、リクエストやレスポンスもgetServerSideProps関数のcontextから取得できることを確認できます。

覚えておくべきこととして、getServerSideProps関数はあくまでNode.js上で実行される関数です。これはブラウザ上では実行されません。

SSRとCSRがうまく共存する形でNext.jsのアプリケーションは動作するようになっています。これが基本的なgetServerSideProps関数の特徴です。

参考

15
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
1