🌄

React Server Componentsに感じたフロントエンドの消失

2021/01/05に公開
8

はじめに

新年早々に面白そうな記事を見つけました。ReactでのAPI呼出しを最適化するために「部分的にサーバサイドで実行するコンポーネントを作る」というもののようです。
https://zenn.dev/erukiti/articles/react-server-components

あるいは去年の記事ですが気になってたものとしてBlitz.jsでReactベースのFWであるnext.jsに永続化層を持たせてRailsのようなFWにしようというアプローチもあります。
https://zenn.dev/mizchi/articles/cbe81299e145491676f8

どちらの記事も書かれてる内容自体は分かる気がするものの 「それをフロントエンドでやる意味あるの?」 というのが拭えずイマイチ腑に落ちなかったんですが、単純に 「私と最前線でやられてる方々で期待してるものがたぶん違う」 という気がしてきたので、その辺を整理のために書いてみます。

注意書き

  • Vue.js/Nuxt.jsは少し触ったことがありますが、React Server ComponentsやBlitz.jsを触ったことは無いです
  • 「なんでそういう方向に進化しようとしてるんだろう?」という疑問への自分の今時点の感想でしかないので何かを否定したり矮小化する意図も無いです
  • 普通に間違ってたり異なる見解もあると思うので、気になることがあればコメント等によろしくお願いします。

TL;DR

  • React界隈はフロントエンドという言葉に捕らわれない進化を目指している
  • ただし、バックエンドが主体となるような大規模システムはそもそもユースケース外
  • XaaSの進化でバックエンドが単なるGWのような構成ならフロントエンド由来の技術でフルスタックFWを作るのは自然

フロントエンドエンジニアなんて居ない

まず最初に少なくとも私の思っていた 「フロントエンドエンジニア」 というロールは無いあるいは最前線には居ない、という事。

元々、フロントエンドエンジニアをJQueryに長けた人たちから始まった 「Webアプリのクライアントサイドを書くロール」 と考えていましたが、たぶんそれはもう間違っています。実際のところは 「React/Vue.js由来の技術を使ってWebアプリケーションを書く人々」 くらいなのかな、と。

であれば、そもそもとしてクライアントサイドに完結する意図が無いから、SSRの利用にも躊躇がないし、なんだったらビジネスロジックも書く。フロントエンド開発がしたい分けではなくWebアプリ開発がしたい ので、フロントエンドとバックエンドの境界なんてどうでも良いのです。UIの記述性が高くロジックも問題なく書けるReact/Vue.jsを使って 「ビジネスロジックも書いてしまった方が効率的」 。このくらいの感覚なんじゃないかなぁ、という気がしています。

このあたりが私のようにReactやVue.jsを 「フロントエンドの技術」 というバイアスで見てるから最近の流れがしっくりこない点かな、と。それらはあくまで 「フロントエンド 発祥 の技術」 というとこまで来てるのに 古い感覚 が抜けてないという話だと思う。

フロントエンド、バックエンドとは?

「フロントエンドエンジニアなんて居ない」 と考えると、そもそも 「フロントエンドとバックエンドって何?」 という疑問が出てきます。
これに関して定義めいたものを見たことは無いのですが、私の元々の感覚としては「フロントエンド==クライアントバックエンド==サーバサイド」でした。ただ、最近のFFBやSSRの流れを見てインフラを境界にするのは合ってないなと思い 「フロントエンドはプレゼンテーションバックエンドはビジネスロジック」 と認識を改めてたところ。

バックエンドにバッチを含めるか否かはさておきとして、この定義だとプレゼンテーション層以外は全部バックエンドだからバックエンドのが広大なんですよね。なので私なんかは 「フロントエンド」 と言われるとカスタマーチャネルだからめちゃくちゃ重要だけどシステム全体としては部分の印象がどうしても強い。

ただ、これは私が仕事では比較的規模の大きなシステムを取り扱うし、フロントエンド/バックエンドとして言葉をわざわざ分けるから感じる事で、たいていのWebアプリやスマホアプリのように プレゼンテーション層が主体 でなんならテーブル設計のような永続化層すらUIに従う 設計ならそっちがメインですよね。

そういったシステムを組む時にはわざわざフロントエンドとバックエンドを分けて考える必要は無いですし、RailsやSpring/JSFで書いて良いならば同様にBlitz.jsなどReact由来のFWで書いても良いのはそうだろうな、と。あとは好みと個別要件の向き不向きですね。少なくともプレゼンテーション層主体のアプリケーションでそこから始まったFWを使おうと考えるのはとても自然です。

フロントエンドとバックエンドはなんで分かれてるんだっけ?

次に思うのは「なにか理由が合って分かれていたはずのフロントエンドとバックエンドを分けずに考えても良いのか?」「それは時代への逆行ではないのか?」 です。では、これについて考えます。

理由はいくつかあるでしょうが主だったところは以下じゃないかな、と。

  • サーバ負荷をクライアントへオフロード
  • DBアクセスなどサーバサイドでしか安全に出来ない処理の分離
  • UIとビジネスロジックを分離して疎結合にする事で

まず、サーバ負荷の軽減というものがあります。これはサーバはステートレスなAPIとして実装し状態管理やHTMLの生成をブラウザ側で実施する事で負荷を下げるという事です。この点に関してはReact Server ComponentsやNext.jsなどSSRのFWであればSPAなどに比べてサーバ負荷が増えますが、状態管理は基本的にクライアント側に移譲出来てますしレンダリングもクライアントとサーバで必要に応じて分散して実行するのでサーバサイドのみで完結するアプローチよりは大きく改善しています。加えてサーバサイドロジックが比較的小さかったりステートレスな事も多いと思うので、FaaSなどのサーバレスなインフラに流し込みやすいのでスケーラビリティを確保しやすい点もポイントではないでしょうか。

次にDBアクセスなどサーバサイドではないと安全に出来なさそうな処理をバックエンドでやる、という考え方。
これに関してはバックエンドの役割が本当に単なるゲートウェイなのであれば従来的なバックエンドは確かに必要無くなるケースが多そうです。何故ならば 直接クライアントからDBにアクセスすれば良い ので。インターネットに公開されているという点はTLSで暗号化してしまえば良いだけです。認証もWebアプリのログインと同程度もに保ててれば問題ないでしょう。権限周りは無論注意が必要ですがそこは選ぶツールと設計しだい。GoogleのFireStoreなんかはまさにそのためのDBだし、GraphQLも レガシーなインフラに対しての汎用的なラッパーという側面もあるんじゃないかと思っています。GraphQLもイマイチ使いどころが見えてなかったのでこの考え方にそうと結構しっくりくる。クライアントサイドJOINを多発するとレイテンシの問題が気になりますが、だからこそReact Server Componentsみたいな発想が生まれるのでしょう。FaaSにDB周りはやらせる方法もあるし。なんか Web以前のクラサバ時代 に戻った気もしますが、歴史は繰り返しますしね。現代のユースケースにマッチするなら戻って何ら問題ない。

最後は責務の分離ですね。
プレゼンテーションとビジネスロジックを分離することでそれぞれ独自にスケールアウトできますし、新しい技術を採用してもインタフェースが守ってれば影響はありません。まあ疎結合にせよというのはシステム設計の基本ですね。
ただこれはある程度システム規模がでかい、または極端なパフォーマンスが要求されるときのことです。
マイクロサービス化と同じですが初期段階での過度な分割はデメリットの方が大きく、プレゼンテーションとビジネスロジックの分離も同様です。

人類は密結合を求めている

「密結合は悪だ! 疎結合のためにAPIの境界を守れ」 と思いますか?
大規模開発を正しくやるためには分割統治が基本で、そのためには明確な境界とI/Fが必要です。古事記にもそう書いてある。最初から正しくやるのが一番! そう思いたくもなります。

なのですが、常に正しくやりたいなら人々はEJB2に満足していた でしょう。悪名高き?EJB2ですがこと疎結合という点では良くできていたと思います。全てをインターフェースで包み単なるメソッド呼出しリモートサーバとのNW通信といった異なる実装を同一の操作でアクセス出来るという非常に美しいアーキテクチャです。

なんですがこのアーキテクチャは大不評と共に無くなりました。それは 全てにインターフェースを書くのがあまりにめんどくさい からです。
たしかにSOAやMSAのような分散システムを組む上ではEJB2のようなデザインは便利なのですが、マシンスペックの向上とともに分散システムでは無く単独のアプリケーションとDBだけでシステムを組むことが増えました。そうなるとインタフェースを書く意味はないですし、分散システム向けのオーバーヘッドが足かせになります。

というわけでアンチテーゼとしてのSpring/Hibernateが流行りましたし、シンプルなMVCとしてのStrutsやRails, PHP系各種FW (SynfonyとかCakeとか)が流行ったという側面もあります。これらはすべて 「単独のアプリケーションで完結する」 ことを基本としたFWです。

疎結合の方が拡張性や保守性は高いのですが基本的にめんどくさいので 分業不要な規模でかつ性能問題が無い ならば常に密結合を選んできたのが人類です。

良く言われる話ですが、この手の疎結合/密結合システムあるいは分散/集約システムはだいたい数年から十数年でトレンドが入れ替わります。おそらくですが最初はスタートアップで密結合なシステムを作り、その会社が業界のリーダーポジション程に大きくなったころに性能問題か分業体制強化のために疎結合システムに移行して成功。他の会社も近いステージの会社が増えてるから同調してトレンド化、そして疎結合システムは面倒なのでスタートアップ界隈を中心に今のニーズにあったシングル性能と生産性の高い密結合なFWが生まれ、彼らが大きくなるまで利用されるという流れかと思います。つまり「今のイケてる会社」のステージに影響してるんだろうなー、と。

ちょうど今がその時期なんでしょうね。たぶん。バックエンドのマイクロサービス界隈もモジュラモノリスがどうのと言い始めたし。この辺はマシンスペックとかインフラの進化とも連動しますし。

私がフロントエンドに求めていたもの

この章はほぼ余談ですが、逆に私が「フロントエンド」という領域に何を期待していたかを書いてギャップをクリアにしたいと思います。

フロントエンドに期待していたものはサーバ負荷の削減非同期処理によるNW速度や描画速度の隠蔽です。
正直に言えば仮に多少クライアントの方がレスポンスが悪いとしても 「フロントエンドに処理を寄せてサーバの負荷が減ったりステートレスになるならその方が良い」 と考えています。これはクライアントでの200msの遅延よりサーバサイドでの50msの遅延の方がチリ積で サーバダウンを招き最終的にユーザ体験を損ねる可能性が高いから です。実際どっちがUXに大きな影響かはケースバイケースだと思いますけど。

また、サーバ側をステートレスにするということはクラウドネイティブ化の容易さにも繋がりますし、クライアント側が厚くなるという意味でもあるので分業もしやすくなります。そのためインフラ面を含めて「フロントエンドとバックエンドの境界」が大事です。

なので、SPAやSSG/JAMStackのようにCDNに静的コンテンツとして配布できるのが最も理想的な形です。CDNはマネージドなHTTPDとして考えると最強レベルの性能とコスパの良さを誇りますからね。クライアントとCDNに負荷を寄せれることの意味はとても大きいです。

次点でBFF (Backend for Frontend)を使ったSSR。これもビジネスロジックを持つ APIサーバとはインフラレベルで分離されているのでフロントエンドとバックエンドの境界は維持されています。フロントエンド側のロジックはFaaSにデプロイしやすいものも多そうですし。

こういう視点だから「React Server ComponentsでAPI統合はもちろんDB直アクセスもできる」とか「Blitz.jsで永続化層を持たせれる」と言われてもピンとこないどころか「筋が悪いのでは?」とすら感じてしまってた要因。
目指してるゴールが違うから当然ですね。

JAMStack向けのFWもありますし、この視点が異端とまでは思ってないですがReact/Vueの進化の方向性として少なくとも異なるゴールが存在しているということは意識しないとなと思います。

まとめ

私自身がフロントエンド主体の人ではないので、実際の認識と合ってるかは分かりませんがなんとなく最近の流れで感じてた違和感がすっきりした気がします。

それ 「RailsやSpring/JSFで良いじゃん。Reactでやる意味あるの?」 って感じるのは当然で解決しようとしてる問題領域が同じだからですね。その上でプレゼンテーション主体のアプリケーションをReactなどそれに向いたFWで書いた方が楽ってのは自然な話。フルスタックFWの範疇にクライアント領域が改めて含まれた だけなので、RubyやJavaでユニバーサルなFWが出ないと同じ土俵では勝負にならないのかな、と。いろんな観点があるから最終的にどれが流行るかは別だけど。

こういう話になる時点で、もはやフロントエンドやバックエンドという区切りはされていないので、React Server ComponentsやBlitz.jsが普及すればフロント領域とかフロントエンドエンジニア という言葉も無くなるのかなぁ、と思ってのこのタイトルとなりました。

それではHappy Hacking!

追記:

Blitz.jsのようなアプローチを採用したときのスマホアプリは気になりますね。PWAやハイブリッドアプリにする感じかな? アーリーステージではそれも良い気がする。

Discussion

teatwoteatwo

springが流行るまでの歴史観には概ね同意なのですが、React Server Componentsの文脈は別の解釈をしています。

性能のボトルネックがクライアントに移動したから再びサーバで改善するという解釈です。SPA(シングルページアプリケーション)が良いとされたのは、それまでのURLを遷移するごとにサーバからロードする旧SSR(PHPやJSP)よりも、クライアントだけで動く方が画面が真っ白にならずに速いからというものでした。しかし、SPAに寄りすぎてクライアントが必要なバンドルファイルやfetch通信の多さにより、今度はクライアント側が性能のボトルネックになりました。そこで、できるだけサーバで事前計算しておけるものはサーバで済ませておけば、クライアントに運ぶ必要があるライブラリも減るしhydrate負荷も減るしコンポーネントマウント時の通信も減るという発想がReact Server Componentsだという解釈です。

では、何がそこまで性能にこだわらせるのかという点ですが、ユースケースがコンシューマー系のウェブにひたすらフォーカスしているからだと思っています。React Server ComponentsやNext.jsの立ち位置はまだまだ未確立なので、日本のインフルエンサーが話していることよりも一次情報の原文を当たる方がよくて、原文ではrailsの代わりのようなフルスタック路線は志向されていません。blogやcommerceのコンテンツ(それは検索結果やSNSからリクエストされる)をクライアントの端末でどれだけ瞬間的に表示できるかが、アジェンダです。kodokiさんが本文で書かれているこの箇所が、まさに原文で確認できる方向性ですね。

SPAやSSG/JAMStackのようにCDNに静的コンテンツとして配布できるのが最も理想的な形です。CDNはマネージドなHTTPDとして考えると最強レベルの性能とコスパの良さ

フロントエンドの消失とはバックエンドのBFFとAPI Backendsの2層化の路線だという意見を支持します。一見、目的が特化している特殊ケースにも思えますが、このスピード第一はjsフレームワークだけでなくブラウザ側でも歩調合わせています。今年の大きなトピックであるWeb Vitalsでも主目的です。私の憶測ですが、これらをリードしている人たちの考えの背景として、ウェブとスマホアプリの棲み分けをそこに見出しているからではないかと考えています。未来を描く人は何人もいて、ウェブにネイティブアプリを移植する方向の人もいるし、スマホアプリとの棲み分けを考えている人もいるという状況認識です。

kodukikoduki

コメントありがとうございます。

SPA(シングルページアプリケーション)が良いとされたのは、それまでのURLを遷移するごとにサーバからロードする旧SSR(PHPやJSP)よりも、クライアントだけで動く方が画面が真っ白にならずに速いからというものでした。

これはおっしゃるとおりですね。SPAあるいはその前身のAJAXにより画面のリロードによるチラつきが無くなるというのが採用の主だった理由だった気はします。ただ、副次的ではあるものの部分的にAJAX
AJAXを使うのではなくUIを丸ごとクライアントに移譲するSPA採用よってテンプレートエンジンやセッション管理が不要なシンプルなAPIサーバに出来るという点もあるので、この記事ではその辺を強調した書き方になってました。補足ありがとうございます。# 認証サーバだけは状態保持しないといけませんが。

SPAに寄りすぎてクライアントが必要なバンドルファイルやfetch通信の多さにより、今度はクライアント側が性能のボトルネックになりました。

こちらやや本論とは外れますが不勉強なので教えて欲しいのですが、fetch通信の多さによりという点はHTTP2ではあまり解決しない問題なのですか?
HTTP 1.1に比べて同時接続数の制限とかはかなり緩和されてる認識ですが。それとも非同期に投げてそれぞれで描画ないしはクライアントJOINではなく、低レイテンシで複数回応答するようなAPIがユースケースとして想定されてるのです?
それで済むならReact Server Componentsは要らないからすまないんだろうなぁ、とは思ってるのですが。

React Server ComponentsやNext.jsの立ち位置はまだまだ未確立なので、日本のインフルエンサーが話していることよりも一次情報の原文を当たる方がよくて、原文ではrailsの代わりのようなフルスタック路線は志向されていません。

おー、そうなのですね。いろんな人の解釈をあたるの良いですが原文は原文で読まないとですね。ご指摘ありがとうございます。ファイルアクセスやDBアクセスも盛り込まれてるのでBlitz.jsと同じくフルスタック志向なのかと思ってました。BFFの範疇を超えないのであればそこまで大きな変革にはならないかもですね。単純に既存のレベルアップという感じで。
まあ、そうであればDBとかファイルに直接アクセスするような境界越え誘発しそうな機能は無くした方が良い気がしますが検討中みたいだし、いろんな方向性を考えてるんでしょうねー。

teatwoteatwo

こちらやや本論とは外れますが不勉強なので教えて欲しいのですが、fetch通信の多さによりという点はHTTP2ではあまり解決しない問題なのですか?

fetch通信の多さとはクライアントjoin的なものを念頭に置いています。BFFやGraphQLのモチベーションにもなっている問題でもありますね。特にコンポーネントマウント時の通信です。

HTTP2なら解消するのではないか?という質問には、正直に申し上げて私はわかりません。React Server Componentsのアプローチは、そもそもコンポーネントマウント時の通信を要らなくできるよねです。基本的には、Next.jsのSSG/ISRの概念がベースになっていて、その上でSSG/ISRをreactのネイティブレベルで提供すれば今のNextよりもっと踏み込んだサーバサイドレンダリングとクライアントサイドレンダリングのオーケストレーション(payloadやhydration)ができるようになるだろうがReact Server Componentsのモチベーションだと理解しています。

kodukikoduki

なるほど。クライアントJOINが多いものはそうでしょうねー。バックエンド側でAPI GW的なアプローチもですがBFFにしろGraphQLにしろNW的に近いところでするのが良いですね。BFFがNW的に近いかはケース依存でしょうが。

クライアントJOINが話題の中心ならHTTP2だと解消するケースは少ないかもですね。マシになる部分はあるにしても。
SSG/ISRをreactのネイティブで提供するとどうなるか、という観点なわけですね。

北山淳也北山淳也

いいまとめでした。
私自身もフロントエンド主体ではない人間なので似たようなオキモチを抱えていました。

フルスタックFWの範疇にクライアント領域が改めて含まれた だけなので、RubyやJavaでユニバーサルなFWが出ないと同じ土俵では勝負にならないのかな、と。

このあたりですが、Rails作者のDHH的には別のアプローチで進めているようです。
これから先の Rails のバージョンアップや別フレームワークとして出てきそうですね。

https://techracho.bpsinc.jp/hachi8833/2020_12_24/102368
https://github.com/hotwired/turbo

kodukikoduki

コメントありがとうございます。
おー、別なアプローチも動いてるんですね。面白そうです。Hotwire見てみます

Shinichiroh TakezakiShinichiroh Takezaki

全体的に素晴らしい内容だと思います。1点、フロントエンドとバックエンドの境界について思うところがあったのでblogにまとめています。よかったらぜひ読んでみてください。https://t.co/RaKm45WRGB?amp=1

kodukikoduki

ありがとうございます! 色んな意見が読んでみたいと思ってたので記事書いた甲斐がありました。読ませていただきますー