Open2
フロントエンド基盤リプレイスで考えたことを都度まとめる
免責事項
- 備忘録、兼社内向け意思決定ログなので多少適当な部分あり
- 精査・検証が足りてない内容も多分に含まれます
アーキテクチャを考える - SSRの是非
事前整理: レンダリング形式のpros/cons整理
SPA(CSR)
- 静的リソースとしてJSを持っておき、リクエストに対してこれを渡す
- 渡された後にjsを解釈し、クライアント側でapi通信やレンダリングを行う。基本はこれを待ってから画面が描画されるため、初期表示は遅い
- 全部読み込んだ後はすべてスムーズに動くのでとても速い
SSR
- リクエストに対して、静的リソースをサーバー側でレンダリングして返す。それ以降の挙動はspa
- 初回のjs読み込みや事前準備をserver, clientのどちらでやるのかの違い
- サーバーでやるメリットは以下
- クライアントの端末スペック、通信環境に左右されない
- apiとのやりとりをサーバー側で行って一通りレンダリングしてから返すため、HTTP通信のオーバーヘッドが減る
- クローラが解釈するタイミングでmetaデータが揃っているのでSEOに強い、と言われてきた。
- 最近はクローラくんがjsを読んでくれるらしいので差がなくなってきてるらしいが、結局ブラックボックスな部分なので安全策としてSSRしとくことが多そうな印象
- クライアントでやるメリットは以下
- node.jsサーバーが必要ないため、
- cdnで配信が可能になる。cdnベースのキャッシュ戦略に乗せやすい。
- インスタンスを用意しなくていいのでシンプルに安い&運用がラク
- reactに関してはclientでもHTMLの生成等を行っていて、その結果が一致しないとエラーになる(hydration error)。windowオブジェクトやlocalstorageなどフロント限定のオブジェクトになんの気なしにアクセスしたりすることで発生する。このあたり対処できないと開発スピードがガタ落ちする。
- node.jsサーバーが必要ないため、
初期表示時に大したことやってないし通信量も多くない & SEOに強くなくていいならSPAでも大きな影響はないが、技術力とお金に余裕があってちゃんと突き詰めるとSSRのほうが体験が良くカスタマイズ性も高い
SSG
- ビルド時にすべてレンダリングを済ませたうえでSSRと同じ動きをする
- SSRにおけるサーバー側のレンダリングフェーズをスキップしてあらかじめ作ったやつを返すイメージ
- 動的にページを用意でき、キャッシュ済みの内容を返し続けるためパフォーマンスがよく、最新の情報を使うページに対しても、定期的なキャッシュの更新を挟むことである程度データの鮮度を高く保ちつつレスポンスを爆速にし続けることが可能。
- ページのレンダリング時にユーザーデータを使えない(ユーザー個別のアクセスに対してページを作るわけではないから)ため、ユーザーデータに紐づくデータを多用するサービスでは効果を発揮しづらい。
- ユーザー個別のデータがサービスのコンテンツと関係がなく、データの鮮度とSEOが重要になる事業に向いてる印象。EC系とかニュースメディア系?
- Next.jsではSSG, SSRの併用が可能で、ユーザーデータが必要な部分だけSSR or CSRすることで最適化が可能。
- ZOZOの基盤リプレイスではNext.js採用してて、このあたりの最適化をやっていた(と思う。記事見つけたら貼る)
個人的に刺さるSPA(CSR)の利点
- CDNに乗せられる
- シンプルに配信でき、運用が楽
- 各CDNサービスの便利な機能に乗っかりやすい
- スケールや増強で考えることが減る
- 開発時の検証やblue/greenリリースの導入などで環境を増やす際も気軽に、かつ簡単に行える。またインスタンスの立ち上げにかかるダウンタイムなども圧倒的に抑えることが可能。
- SSRにどうしてもついて回る開発の複雑さや様々な制約を、ある程度無視できる
- チームの技術的成熟度が高くない場合にとくに有効
- 開発時はどうしてもSSRになってしまうことが多いので、逆にこのあたりの環境の差でハマりそうだが、だいぶマシになると思う
聞いた話
- server componentsのいいとこ→hooksを覚える必要がない
- reactの難しさの大きな部分をhooksが占めている
- 学習コストや実装コストがserver componentsに寄せることで下がることは期待できる
SSRしない場合の懸念
- クライアントの実行量が増える
- 特に想定顧客層が30代以上とかだと端末スペック的に不安な印象
- すべてのAPIコールをクライアントサイドでやる羽目になる
-promise.allSettled()
などを適切に使って、なるべくオーバーヘッドを少なくしよう
- むじゃきにawaitしたら死につながる
- BFF層を作って、複数のAPIをまとめる & DTO変換層を持っておくことで、api叩く回数を減らしclientでやることをとにかく減らすなども候補に上がる