Help us understand the problem. What is going on with this article?

SSRはおまいらには早すぎた 〜Next.jsのgetServerSidePropsの登場が何を意味するか〜

概要

Next.js 9.3から getServerSideProps という仕組みが導入されました。

https://nextjs.org/docs/basic-features/data-fetching

上記がそのドキュメントですが、これを読んで感動した私はつい語りたくなってしまいました、私情が多分に含まれております、ああそういう視点もあるんだくらいに見ていただければと思います。

僕自身は、SEOをあまり気にしないいわゆるSPAっぽいWebページのNext.jsでの開発を1年ちょいくらい業務でやっていて、今もNext.jsユーザーです。

  • getServerSidePropsって何が良いの?なんで生まれたの?」
  • 「なんでgetInitialPropsは非推奨なの?」
  • 「Vercel製のSWRってライブラリあるけど、あれ何?」
  • 「てかgetServerSidePropsとSWRって関係あるの?」

この辺の疑問をお持ちのNext.jsユーザーの方は是非ご一読ください。

※過剰な煽りタイトルであることを自覚していますので、先に謝っておきます、すみません。

TL;DR

僕の主張は

  • getInitialPropsをやめてgetServerSidePropsを使おう。
  • 認証が必要なページ(もっと言えばSEOを気にしないページ)ではSSR時に(=サーバサイドで)認証が必要なAPIコールをするのをやめよう。
  • そうするとクライアントサイドでのAPIコールが重要になるから、Vercelが公式に支援してくれるよ、それがSWRだよ。

これによって、Next.jsはよりシンプルでより強力なフレームワークになります。

getServerSidePropsとは?

getServerSidePropsとは、「getInitialPropsをサーバサイドだけで実行するようにしたもの」です。


サーバサイドでだけ?じゃあブラウザ上でページ遷移したときどうなるの?

って思うと思うんですが、これはNext.jsの頑張りで解決されます。
なんとブラウザ上でページ遷移するとサーバにリクエストが飛び、サーバサイドでgetServerSidePropsが実行され、ブラウザはその結果のpropsの中身をjsonで受け取り、通常通りレンダリングを続けます。
これに関してはユーザーは一切設定の必要がなく、関数を書くだけで実現出来ます。
Next頑張りすぎ案件なんですが、なぜここまでするんでしょうか。それも合わせて続きを読んでみてください。

今までページのトップのコンポーネントに渡すPropsはgetInitialPropsを使っていましたね。
getInitialPropsはSSRでもCSRでもページの表示時に同じように実行されます。めっちゃUniversal JS感ありますよね。
getInitialPropsの中でAPIリクエストとかを書いておくと、SSRしてもCSRしてもレンダリングの最初からデータを取得できるわけです。クール。

ところが、こちらがgetInitialPropsのドキュメントなんですが、getServerSidePropsの登場により、公式に「getInitialPropsよりもgetServerSidePropsの方がオススメだよ!」という状態になりました。

Recommended: getStaticProps or getServerSideProps

If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.

These new data fetching methods allow you to have a granular choice between static generation and server-side rendering. Learn more on the documentation for Pages and Data fetching:

なぜ公式にgetServerSidePropsを推すのか?

一見getInitialPropsを機能制約しただけのように見えますよね。
なのになんで推奨?

(あまり具体的にこの理由が書かれていないのはなぜだろう、もし公式の文章とかあったら教えてください。)

上記の公式ドキュメントで何気に最も重要だと思うのはこのfetching-data-on-the-client-sideの項目の次の文です。

この項目は、getServerSidePropsをどんな場面で使うべきか?という項目からもリンクされています。

This approach works well for user dashboard pages, for example. Because a dashboard is a private, user-specific page, SEO is not relevant and the page doesn’t need to be pre-rendered. The data is frequently updated, which requires request-time data fetching.

「認証が必要なページはSEO関係ないし、そういう頻繁に更新されるデータってレンダリング時というよりは随時更新した方が良いし、全部ブラウザ上でAPIコールすれば良いよね」というようなことを言っていて、上記のgetInitialProps非推奨と合わせると、それはつまり「Next.jsは認証が必要なページに関してはSSRでデータ取得を行うのはやめた方が良いと思ってるよ」ということなんですよね(と、僕は解釈しました)。

つまり、SSRでページのメインコンテンツのレンダリングが必要なのはSEOを意識するページだけ。いや、うん、当然すぎるんだけどね...

以下で詳しく説明します。

SSRのつらみがどこから来るか

SSRとCSRが混合されたSPAを作るのって非常に難しいんですよね。

基本的にNextが色々と環境(サーバ or ブラウザ)の差分吸収をしてくれるとは言え全てを隠蔽してくれるわけではないので、依然としてUniversal JSを意識して書かないと死ぬし、「ここからSSRしてここにCSR(クライアントサイドレンダリング)するとReduxStoreのこのプロパティがこうなって...」みたいなやけに細かいことを考える必要が出てきたりする。

その中でも、特に色々なものをややこしくするのが、SSRからの認証ありなAPIコールです。

SSRから認証情報付きのAPIコールをするためには

ブラウザはリクエスト時に自動でCookieを付与してくれますが、サーバにはそんな機能はありません。
ので、ブラウザから来たリクエストの中に含まれる認証情報を取り出して、サーバからのリクエストにもそれを付与する必要があります。

これだけだと大してツラくないように見えますが、自分らでCookieをいじったりあとはサービス特有のヘッダをforwardingしたりとか色々実運用のための実装を載せていくと結構カオスになります。

サーバでもブラウザでもメインの処理が走る

それだけでなく、APIコールを行うということはそれはつまりほとんどの場合ページのメインコンテンツをレンダリングするということであり、ページそのもののレンダリングがサーバでもブラウザでも行われるということを意味します。

純粋に、実行環境が一つから二つになるだけで複雑度は非常に高くなります。
CSRでは起きないけどSSRだと起きる問題、またその逆、みたいなのが出てきます。

getInitialpropsの功罪

getInitialPropsはサーバとブラウザの差分を吸収してくれる画期的なAPIだったわけですが、これがカオスを呼び込みました。
つまり、サーバでもブラウザでも実行されるので、getInitialPropsの中に必然的に認証が必要なAPIコールが登場してしまうわけです。
そうなると、SSRでも認証が必要なAPIコールを行うのが自然になり、上記のように複雑度が高まっていきます。

クライアントサイドで認証が必要なAPIコールをするのは自然ですし、ページのトップコンポーネントにPropsを渡す方法はgetInitialPropsしか無かったので、これは当然の流れでしょう。
(ていうか、公式のドキュメントでもgetInitialPropsの中でAPIコールをしていました)

でも、上記で書いたように、SEOが関係ないページではメインコンテンツのレンダリングをサーバサイドで行う必要は無いんです。
無駄に複雑度を高めるだけ。

getInitialPropsの非推奨、そしてgetServerSidePropsの使用は、この自然にサーバサイドで認証ありなAPIコールをしてしまうことを防ぐ効果があります。

ちなみに、もちろんgetServerSidePropsの中でも認証ありのAPIコールは実行可能です。
引数でcontextというオブジェクトが渡ってくるので、context.req.~~とかやるとブラウザからの情報を取得できるはずです。
それでも、何も考えなくても入り込んでしまうgetInitialPropsよりはそのわざわざ感ゆえに防ぐ効果は十分あると思いますし、ライブラリとしてそういう思想なんだぞ、というのを実装で主張するというのは大事なことだと思います。

そもそもそういう認証が必要なAPIコールで取得するようなデータってSSRで提供する必要なくない?

そしてもう一つ同時にVercelがサーバサイドでの認証ありAPIコールをやめた方が良いと思っている理由は、「頻繁に変わるデータ、わざわざ初回のレンダリング時に頑張ってサーバで取得する必要なくない?だって更新するたびに何度もブラウザで取得したりするじゃん」っていう意見です(これはちゃんとドキュメントに書いてあります)。
(もちろんこれはSEOが関係ない、つまり本質的にSSRをする必要が無いページにおける話です)

上記で引用したような、privateでかつuser-specificなデータというのは、つまり一般的なログイン機能が存在するSPAを想像してもらえればわかりやすいですが、ブラウザ上で編集されたり他のユーザーが投稿したりと頻繁に更新され、頻繁に取得されます。
(「頻繁」という言葉の理解を進めるために、対比としてブログの文章データなんかを想像して頂ければと思います)

それをわざわざ初回レンダリング時に1回取得するために、頑張ってサーバサイドでAPIコールする仕組みを整える必要って、本当にある?

ブラウザで全部やろうよ

だからさ、そういうデータの取得は全部ブラウザでやろうよ。
そのために便利なライブラリ置いとくからさ。名前?SWRって言うんだけど。

https://nextjs.org/docs/basic-features/data-fetching#swr

ということでVercel公式のデータフェッチ支援ライブラリがオススメですよ。
SWRの良さについてはまた別記事で書こうと思うのでそこまで詳しく書きませんが、SWRは上記のようなデータの「取得」に関してとりあえず使っとけとなるレベルには楽に高機能を実装できるライブラリです。
ReduxとかStorybookと一緒に使う場合はどうするの的な問題は別でありますが。

データの「取得」

SSRから認証ありのAPIコールをなくす場合、影響を受けるのってデータの「取得」だけですよね。
SSRではデータの「更新」が行われることは通常ないからです(もしあったとしたらそれはブラウザからのGETリクエストに副作用があるということになるので、やめた方が良いです)。

SWRが支援しているのはデータの「取得」のみであることも、この文脈で納得出来ます。

厳密にはSSRをなくすわけではない

SSRをするとかしないとか書いてますが、厳密にはNext.jsで書く以上SSRは必ずされます。
ただし、APIコールがなくメインのコンテンツがサーバサイドでレンダリングされない場合、基本的にはSSRで返ってくるHTMLはApp Shell(Webアプリケーションのガワ)だけ、みたいな感じになるでしょう。
それはそれで良くて、つまり僕が言いたいのはSSRそのものをなくしたいというわけではなく、SSR起因の不要な複雑さをなくしたいという意味なのです。

まとめ

こうしたVercelの方針を加味したうえで、Next.jsを使ううえでの僕の主張はこうです。

  • 基本的にSSRをするとコードが複雑になるので、避けられるなら避けるべき。
  • SSRが必要なのはSEOを意識するWebページだけ。
  • SEOが不要なページであれば、認証ありなAPIコールは全てブラウザ上で行えば良い。
  • 不用意な混入を防ぐために、getInitialPropsをやめてgetServerSidePropsを使おう。

感想

なんでこれに感動したかと言うと、自分自身が当然のようにgetInitialPropsの中で認証ありなAPIコールをしていたからなんですよね。
「SSRは人類には早すぎたのだ!」と僕はなんど思ったかわからないという感じなんですが、SSRという技術の使い所がどこなのかを、Next.jsは実装で示していると思うのです。
SSRがどういう場面で必要かということは理解していましたが、改めてハッとさせられました。

Next.jsの登場でSSRが手の届きやすい存在になってなんとなく導入する、みたいなのがこれからもあるんじゃないかなと思うので、それには警鐘を鳴らしていきたいという気持ちでした。
あなたのそのSSR、本当に必要ですか?

蛇足

ちなみに、getServerSidePropsの紹介のブログにはこんな文章もあります。

This increases performance in many cases as the server will generally have a faster connection to the data source. It also increases security by exposing less of the data fetching logic.

(和訳)サーバーは通常、データソースへの接続が高速になるため、多くの場合、これによりパフォーマンスが向上します。また、公開するデータフェッチロジックが少なくなるため、セキュリティが向上します。

前者に関してはNext.jsが動いているサーバとデータストアのサーバが近いことが前提ですが、このメリットももちろんありますね。
もしもこのメリットをprivateでuser-specificなデータを扱うSPAでも享受したいと思ったら、挑戦しても良いのかもしれません。
少なくとも現時点での僕は絶対にやりたくないですが...

ryokkkke
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした