フロントエンド大集合!うひょさん&あっきーさんと語る!Next.jsお悩み相談室のトップ画像

フロントエンド大集合!うひょさん&あっきーさんと語る!Next.jsお悩み相談室

投稿日時:
うひょのアイコン

株式会社カオナビ / フロントエンドエキスパート

うひょ

Xアカウントリンク
あっきーのアイコン

フロントエンドエンジニア

あっきー

Xアカウントリンク

本記事では、2025年5月14日に開催されたオンラインイベント「【技術選定を突き詰める】Online Conferenc​​e 2025」内のセッション「フロントエンド大集合!うひょさん&あっきーさんと語る!Next.jsお悩み相談室」の内容をお届けします。同セッションでは、株式会社カオナビのうひょ(@uhyo_)さんとあっきー(@akfm_sato)さんに、Next.jsの魅力と活用方法をお話しいただくとともにみなさまのお悩みにも回答していただきました。ぜひ本編のアーカイブ動画とあわせてご覧ください。


「Next.jsと状態管理のプラクティス」/うひょ

うひょ: 株式会社カオナビでフロントエンドエキスパートをしている、うひょです。Reactのフレームワークで何がいいかと聞かれたら、とりあえずNext.jsというタイプで、いつの間にかNext.js推しです。

Next.jsにApp Routerが登場し、サーバーコンポーネントが活用できるようになったことで、フロントエンドにおけるNext.jsの状態管理の考え方も見直す必要があります。そこで今回は、Next.jsのApp Routerを前提とし、React Server Componentsの存在を考慮した状態管理の方法を解説します。App Routerが登場してから時間が経ちましたが、その本格的な使用をためらう方もいらっしゃるのでApp Routerに抵抗がある方々を主なターゲットとし、Reactの状態管理にどう立ち向かうべきかというお話しができればいいなと思います。

これは以前の記事の復習ですが、React Server Componentsは多段階計算として捉えられるというお話をしました。この考えは現在でも有効だと思っています。具体的には、React Server Componentsが入ったアプリというのは、2段階で描画されます。まず1段階目でサーバーコンポーネントがレンダリングされ、その次の段階として、クライアントコンポーネントがレンダリングされる、という流れです。

各コンポーネントの特性と活用術

サーバーコンポーネントとクライアントコンポーネントは特性が異なります。サーバーコンポーネントはいくつか特徴があり、一つはサーバー側でキャッシュが利くことです。サーバー側でデータを使ってコンポーネントをレンダリングする際、サーバー側のキャッシュを活用できます。あと、サーバーでコンポーネントのレンダリングを行うので、クライアントの計算が削減できます。これによりいわゆる転送量、JavaScriptのソースコードをどれだけフロントエンドに送るかというところも削減できるケースも多々あります。 ただデメリットもあります。例えばサーバーコンポーネントを再レンダリングする場合、結果の取得にネットワークを経由する必要が出てきます。その反面、クライアントコンポーネントの特性として、サーバーを介さなくてもフロントエンドの計算だけでコンポーネントの再レンダリングが可能になります。その裏返しとして再レンダリングだけでなく最初のページ表示にもJavaScriptをクライアント側で実行する必要があります。 元々いわゆるSSRで初期HTMLが用意されていても、ハイドレーションを実行するためにクライアント側でちゃんとJavaScriptを実行する必要があります。これは従来のReactの考え方と同じですが、新しくサーバーコンポーネントが登場したことによって、クライアントコンポーネントの相対的なデメリットになったと思います。

各コンポーネントの使い分け:現代のWeb開発における「アイランドアーキテクチャ」

どう使い分けをするのかというのはやっぱり自然な疑問ですよね。私が紹介する考えというのは、土台となる部分にサーバーコンポーネントを使うということ、そしてその上でインタラクションが必要な部分にクライアントコンポーネントを使うということ、これが基本的に有効だと思っています。いわゆるアイランドアーキテクチャと同じ考え方で、今回あっきーさんと共演ということで彼の記事から引用をさせていただきました。React Server Componentsも実はこれに近い考え方なんです。なぜならこのReact Server Componentsでは、従来ステートとして保持していたサーバーからのデータが、インタラクションに直接関係しないという場合もあるんですね。なのでクライアント側のステートではなく、サーバーコンポーネントにデータ取得を担ってもらうことで、クライアントのステートを削減できるという考えです。

昔のフロントエンドではサーバーが出力したHTMLを土台にJavaScriptでHTMLをいじりクライアントサイドのインタラクションを実現する、みたいな世界観がありました。やがてこれが一周回り、進化して再現されたのがReact Server Componentsです。この考え方はおすすめです。特にApp Routerに慣れていない方は、この考え方から入ってみてもいいと思っています。

今回の実例:ToDoアプリ

ここからはToDoアプリを実例にお話させていただきます。AIでToDoアプリばかりを作ってると揶揄されたりもしますが、要点を押さえてゼロイチを出すにはとても便利なので題材にしてみました。

Next.jsでToDoアプリを実装する場合、どうやって実装しますか?App Routerの機能を最大限に使うようAIにリクエストをした結果、大体こんな感じの設計になります。まずToDoの通常のデータをサーバーコンポーネントから取得してくる、当然ですが、ToDoを更新したときはこのrevalidatePathというのを使い1回キャッシュを破棄するという実装になります。そうするとサーバーコンポーネントが再レンダリングされ、ToDoの更新が画面に反映されます。

このアーキテクチャはいい感じに動くもののrevalidatePathには問題があります。確かにキャッシュを消せますが、ページ全体のサーバーコンポーネントが再レンダリングされてしまいます。ToDoのチェックボックスが一つ変化するだけなのに、画面全体を受信するのでは無駄が大きいです。ネットワークの経由が必要になっているのもよくわかりません。revalidatePathはURLパスに基づいてキャッシュを無効化しますが、データ変更とURLの密結合的な管理はしたくありません。revalidateTagを使えばデータとタグを紐付けられるので、revalidatePathよりは多少マシですが、それでもアプリ全体でキャッシュキーの管理が必要になり、これも理想的ではありません。

そこで有効なのが、差分だけをステート管理するという考え方です。つまり、ToDoのデータ自体はサーバーから取ってきたものをそのまま使い、ToDoがチェックされているのかという状況だけをクライアントコンポーネントで管理します。そうすると余計な通信を発生させずに反映できます。これはサーバーコンポーネントを活用していることが前提ですが、これはさっきの土台という考え方と噛み合うのでおすすめです。 差分だけステート管理する.png 実際のコードでは、サーバーから来たallToolsというデータと、ローカルステートとしてcheckedStateを組み合わせると画面に描画するのに使えるfinalToolsができ、差分だけクライアントのuseStateで管理するという流れになります。この例を通じて紹介したかったのは、サーバーから送られてきたデータ(今回はToDoの一覧)を土台と捉え、その上にチェックボックスのインタラクション部分だけをクライアント側で実装するという考え方です。

今の例に加えもう一つの例を見てみましょう。先ほどのToDoアプリに無限スクロールを実装するとします。無限スクロールというのは、リストを一度に描画するのではなく、下までスクロールしたらサーバーに追加のデータを要求しつぎ足していく機能です。これを実装すると、必然的にサーバーとの通信が発生しますが、サーバーコンポーネント全体をレンダリングしちゃうと、既にダウンロード済みのところまで再度取得することになり良くないんですよね。

なので無限スクロールでは差分だけダウンロードしたいですよね。この場合もステートを管理するという考え方が有効です。今回はサーバーからは最初の1ページだけサーバーコンポーネントのレンダリングの一環としてレンダリングし、それをクライアントの初期ステートとして使用する方法を紹介します。 やっぱりステートで差分を管理する.png userActionStateを使った無限スクロールの実装では、dispatchでサーバーからgetTodosを呼び出します。これにより既存のリストにサーバーから得たToDoを追加し全体のリストを得るという方法です。userActionStateに渡す初期状態として、サーバーコンポーネントから取得したデータを渡すことで、クライアントサイドの無限スクロールに、サーバーサイドのデータをいい感じに組み込むことができます。

ということでまとめです。今回のトークはToDoリストを例にNext.jsの状態管理のプラクティスの一例を紹介しました。これはサーバーコンポーネントをページの土台として使うという考え方です。この方法はいつも正解とは限りませんが、特にApp Routerの全機能を使うのが怖い方にはNext.js入門として有効です。これによりサーバーコンポーネントを活かしつつ、慣れることができると思いますよ。

「Next.jsを選ぶ恩恵と制約」/あっきー

あっきー: 今日はNext.jsを技術選定として選んだときの恩恵と制約の話をしたいと思います。小難しいことを言っていますが、要はNext.jsを選ぶってどういうことなのかという解像度を上げるヒントになればいいなと思っています。

あっきーです。TwitterやZennではakfmという名前とアイコンで活動してます。フロントエンドエキスパートとして、いろんなチームのお助けや実装、技術選定などフロントエンドの便利屋さんみたいなことをしてます。Next.jsやReactまわりテストの話とかもよく記事に書いてます。あんまり公にはしていませんが、組織開発や開発プロセス、アジャイルとかも結構好きです。

今日のテーマは技術選定とNext.jsです。Next.jsを選ぶ際に考慮すべき点やそうでない点について僕なりの話をお話しします。既に知ってる人からすると、物足りないかもしれませんが、Next.jsの選択を悩んでる人って結構多いイメージがあったので、この話をしていきたいと思います。 技術選定の恩恵と制約.png 僕が普段考える技術選定とは、やはり恩恵と制約のバランスに尽きます。一番大きな恩恵を得られる、かつ制約が許容できるということが選ぶ条件だと考えています。恩恵ばかりに目がいき、いざ採用してみたら「違った」「しんどい」と言って違うフレームワークを選び直すみたいな話もよく聞くんですが、万能なフレームワークはそんなにありません。特にフロントエンドに関しては特色や得られる恩恵も伴う制約も異なります。それらの兼ね合いをきちんと比較検討して選ぶことが、フロントエンド、特にReact系のフレームワークにおいて大事になってくると思います。

技術選定は、様々な要素を考慮する必要があります。例えば、短期的な開発効率と中長期的な開発効率は異なる視点です。初期段階では迅速な開発が重要ですが、後には破壊的変更の頻度なども考慮が必要です。そのほかインフラやシステムの制約、開発規模、現メンバーのスキルセット、さらには採用マーケティングやモチベーションといった観点でフレームワークやライブラリの選定をしなくてはいけません。特にフレームワークのような大きな選択では、採用マーケティングや開発効率インフラ制約への影響が大きいため、慎重にならないといけません。

Next.js 4つの恩恵

Next.jsを選ぶとどのような恩恵が得られるのか。もちろんいっぱいありますが、できるだけ代表的なところを4つほど選びました。

1つ目は、一番人気があるフレームワークだと思います。人気があるということは、エコシステムや知見が充実しているということです。新機能開発をはじめ、関連するライブラリも多くあり、問題解決しやすいのはもちろん、AIによるサポートもあります。実際Vercelはそこを狙っているようで、Next.jsが一番人気になることで、簡単にデプロイできるVercel、少々のチューニングでいい感じに作ってくれるV0、という形の世界観を目指しているようです。やはり人気のフレームワークを選ぶというのはいろんなメリットがあるのだと思います。

2つ目は破壊的変更に対する慎重な姿勢です。Pages RouterからApp Routerへの移行時には混乱もあったと思いますが、現在でもPages Routerは最新バージョンで問題なく動作し、脆弱性対応もされています。さらにPages RouterとApp Routerで共存できるので、App Routerへの移行は段階的にできます。特定のパス配下だけApp Router、残りはPages Routerみたいに少しずつ移行でき、そういう移行パスが結構揃っているのも大きな強みであり、他のフレームワークと比較しても中長期的な安定性を割と期待できます。

そしてやはり言われるのはデフォルトで高いパフォーマンスです。最初は高いパフォーマンスを求めていなくても、だんだんスケールするにつれパフォーマンスが劣化し、最終的な最適化が大変だったり、フレームワークの変更を余儀なくされることがあります。そう考えると最初から高いパフォーマンスを維持する手段と、最適化もされてるNext.jsは、高いスケーリング特性を持つと言えるでしょう。

最後に優れた開発者体験です。割とどこもやっているところだとは思いますが、それも含めて4つ、Next.jsを選ぶと得られる恩恵というのがあると思います。

Next.jsの知っておきべき4つの制約

では、Next.jsを選ぶ上での制約は何でしょうか。現在Next.jsを選ぶ場合、Pages Routerは動作しますが、新規機能開発はされておらず、一部のバグも未修正なこともあります(脆弱性対応はされています)。そのため今やるなら間違いなくApp Routerから始めるべきです。そうするとReact Server Componentsに対する理解とその構成に乗っかるということは必須になってくるので、「React Server Componentsが大嫌いです」という方はNext.jsを選ぶべきではないでしょう。ただ他のReact系フレームワークもRSC対応を進めている段階なので、RSCへの理解は今後ほとんど必須になっていくと考えられます。

2つ目の大きな制約はインフラです。Vercelであればあまり問題はないのかもしれませんが、僕自身がAWSのECSやApprenderにデプロイする際には困ることもありします。また巷だとCloudflare周りで困ってる人も多いようです。アダプター導入など、積極的に改善しようとしていますが、やはりまだまだインフラ制約はあると思います。

あとは実装品質への不安も挙げられます。たまに内部実装に疑問を感じことがあり、脆弱性とかを見ても、センシティブな実装だと思うときもあります。その後React Routerで同様のヘッダー改ざん系脆弱性が出たので、他でもあるのかなと思ったり。しかしNext.jsが非常に堅牢に作られているかといえば、センシティブな実装がある点は、ちょっと不安だなと感じる部分です。

そしてエコシステム系、特にWebpack依存です。他は割とViteをベースにフレームワークを作ってるのに対し、Next.jsはWebpackを使っているため導入しづらかったり対応が必要になってしまいます。WindowsでのPostCSS関連の問題が出てきてしまうこともあります。ただ他のフレームワークにも同様の制約がないとは限らないので、他の選択肢とも比較検討する必要があるでしょう。

またNext.jsを選ぶ際に、将来的な人気が見込めるかどうかは、あまり関係ない、末節な論点だと僕は考えます。今後の人気を断定することはできないので、ここら辺はあまり理由にしない方がいいでしょう。

なお「高いパフォーマンスが必要ないからNext.jsは不要」という考え方は短絡的すぎます。パフォーマンスはいろんな観点がある中のひとつです。これを理由にNext.jsを避けるのは、他の要素を考慮していないと言えるでしょう。そもそも「パフォーマンス不要」という発言は知識不足からくる場合も多く、劣化してから困ったり、ゼロイチを出す段階で遅くなったりすることもあります。後からパフォーマンスのチューニングを行うのは非常に困難で限界があるため、パフォーマンス観点は技術選定の初期段階でこそ大事だと思います。

あとはDBアクセスですが、DBアクセスの有無はNext.jsで言うと、正直あまり関係ありません。Next.jsはフルスタックフレームワークではないため(DBアクセスの有無、API経由、ファイル読み込みなど)DBアクセスするか、APIを別にするかどうかはNext.jsの選定後に検討すべき設計上の選択肢だという話ですね。 まとめ.png まとめです。これまでお伝えしたように、Next.jsには選ぶメリット、選ぶ上での制約があります。Next.jsを選ぶ際はぜひ念頭に置き、自分たちの選択として最適なのかを考えていただけたらいいなと思います。

うひょさん&あっきーさんと語る!Next.jsお悩み相談室

モデレーター: それではこれよりお悩み相談室を始めます。Next.jsをつい先日始められた方より。お二方が初心者におすすめする勉強法を教えてください。

うひょ: Next.jsは提供されるAPIが多くて、どれが新しくどれが古いのかが分かりにくく、初心者には学習のハードルが高いと感じるのも無理はありません。なので、最初からすべてを完璧に使いこなそうしないことが一番大事だと思っています。まずは最低限の知識で開発を始め、そこからNext.jsの機能を活用して改善点を探っていくのが効率的な学習方法でしょう。そしてNext.jsの公式ドキュメントをちゃんと読み、まだ使いこなせてない機能を把握し、少しずつ知識を深めていくのが効果的です。

あっきー: そうですね、うひょさんが言ってくれたように、まずはできるところから自分で調べながら進めるのが良いでしょう。もし学習にまとまった時間が取れるなら、Next.jsの公式チュートリアル(Next.js.org/learn)が非常に充実しています。僕の著書も学習の一助になるかと思いますので、ぜひ参考にしてみてください。

モデレーター: では二つ目。Next.jsのキャッシュに関する動作の部分がわかりにくいので、とりあえずの回避策などはありますか。

あっきー: もうこれは一言。App Router直下のlayout.tsxでexport const dynamic = 'force-dynamic'という設定をforce-dynamicにすればサーバー側のキャッシュが軒並み無効化されます。まずはこの状態で開発を始め、必要に応じてキャッシュ設定を見直していくのが良いでしょう。

うひょ: 私もキャッシュをオプトアウトするのはとても有効だと思います。昔ながらのやり方に近くもありますし。Next.jsのパフォーマンスを最大限生かすならキャッシュの最適化が重要ですが、Next.jsの基盤に乗っていれば、キャッシュは後からでも導入できます。なので最初にオプトアウトし、必要になったときに調べるという考え方はいいと思います。たしかに、Next.jsのキャッシュの仕組みは複雑でわかりにくいという問題はあります。これについては、やはり一度じっくりと仕組みを学習する必要があります。あっきーさんの解説記事などを参考に学ぶのも良いでしょう。

あっきー: キャッシュ周りに関しては、use clientのような機能が導入されるようで、そうなればキャッシュの扱いが大幅にわかりやすくなるはずです。

うひょ: そうですね、個人的にも早く入って欲しいですね。

あっきー: 今はデフォルトでキャッシュする世界観ですが、use clientは必要なときにキャッシュを宣言する、これは自由度が高い設定方法ですね。僕含め割とキャッシュアンチの人にも、すごく良いと思います。

モデレーター: では三つ目です。エコシステムが未完成な現状で、App Routerを導入するタイミングを悩んでいます。クライアントコンポーネント過多を避けつつ、最適なタイミングとアプローチについて教えてください。

うひょ: 今日のトークをまとめると、Pages Routerではなく、App Routerは最初から使うべきというのは私も同意見で、間違いありません。その一方でApp Routerの全機能を完璧に最初から使いこなすのは現実的ではなく、例え最初はuse clientばかりになったとしてもApp Routerを使わないよりはその状態でも導入する方が意味があります。App Routerの利点は使い始めてから、徐々に最大化する方法を探っていけば十分です。

あっきー: うひょさんと同じく「まずApp Routerを使うタイミング」だと思っています。もはやなぜApp Routerを使うのかという議論よりも、なぜ使わないのかと言われる段階に来ていると思うので。一方、React Server Componentsの土台の考え方、組み合わせ方、テスト戦略など何層かの学ぶべき点があります。ただ全機能をいきなり完璧に使いこなすのは無理なので、試行錯誤しながら調べながら使っていくのがいいでしょう。もしこの議論がチーム内で再燃したら、今日の発表や、うひょさんの記事、世の中にあるReact Server Componentsに関する情報などを共有し、use clientばかりになるという課題を少しずつ解消していければと思います。

モデレーター: 最後の質問です。フロント・バックを現段階で分けておらず、一人から二人で運用しているシステムに対してNext.jsへのリプレースをかけるのは過剰な気がします。少人数体制でのNext.js導入について、どう考えるべきでしょうか?

あっきー: まず過剰かどうかで言うと、そんなことはないと思います。事例の多くが複数人規模に見えるかもしれませんが、ポートフォリオでNext.jsを使ってる方は多くいらっしゃいますし、それこそToDoアプリの延長線上で作ってる方もいるので、Next.jsが少人数開発に向いていないという問いにはノーと言えます。 一方で、「過剰」に感じるかどうかは、現在お使いのフレームワークとの比較次第です。例えばRailsのようなORMやデータベース周りまで含むフルスタックフレームワークから移行する場合、自分たちで設定・管理する範囲が増えます。やはりやることが増えると、過剰に見えたり、工数感が大きく感じられる可能性はあります。結局は「何をとって何をするか」という話になり、Rails運用で課題がありNext.jsへ移行したいのであれば、その恩恵のために、ある程度の工数が増えてもやるメリットがあるかというのが選ぶ基準になるでしょう。

うひょ: 私が強調したいのは、まさにあっきーさんの今日のトークであった、Next.jsはフルスタックフレームワークじゃないということですね。Next.jsを完全にフルスタックフレームワークとして運用しなきゃいけないものだと考えると、特にフロントとバックを一人で運用するようなシステムに導入するのは「過剰」だと感じるのも無理はありません。しかし、Next.jsはフルスタックフレームワークではないので、そのように運用する必要はありません。

極論はNext.jsもただのReactアプリを動かすためのReactのフレームワークなんです。App Routerの導入で、サーバーという概念が加わり面倒くさいんですけど、本来Next.jsが目指しているのは、フロントエンド用のサーバー、いわゆるBFF(Backend For Frontend)的な立ち位置だと思っています。そのためNext.jsがサーバーを持つことを過剰と判断するなら、本格的なバックのAPIサーバーとは別にフロントエンドのためにサーバーを立てる必要があるかという議論になるでしょう。私自身も、既存のReactアプリをNext.jsに移行する際、サーバーが増えることが一番ネックになると感じています。

なので、Vercelのようなサービスを使えばサーバー管理の手間をほとんど増やさずに、少人数でもNext.jsを運用できるでしょう。しかし、サーバー管理のコストが大幅に増えるなら、「過剰」だと判断されるかもしれません。そのためNext.jsが具体的にどういう役割を果たすのか、何のために使うのかという目的まで含めて判断することが必要なんじゃないかなと思いました。

あっきー: そうだなぁと頷きながら聞いていました(笑)

モデレーター: ではどのような場合にNext.jsを選ばない方がいいのかについてのご意見も伺いたいです。

あっきー: 僕は選ばないことがないので(笑)

うひょ: もしこれまで純粋なフロントエンド開発をしてきて、新しいサーバーを立てることに抵抗がある場合は、私ならNext.jsを選ばないかもしれません。一応Next.jsにもStatic Export機能があり、サーバーなしでも使えそうですが、その機能でどこまで頑張れるのか不安を感じるため、採用を見送るでしょう。

モデレーター: use clientがクライアントコンポーネントになる認識でしたが、クライアント側で処理されない場合はあるかというご質問です。あ、お二方ともうなづいていらっしゃいますね(笑)

うひょ: はい、公式の説明では、use clientはそのクライアントコンポーネントの世界の入口です。つまり、use clientと書かれたファイル内のコンポーネントは間違いなくクライアントコンポーネントになり、そこからインポートされた別のコンポーネントもすべてクライアントコンポーネントとして扱われます。そのためページの最上位コンポーネントにuse clientと書くと、すべてのコンポーネントがクライアント側で処理されることになります。これは、前のお悩みにあった「全部use clientにしたらApp Routerを使う意味がないのでは」という点にも繋がってきますね。

あっきー: 一応use clientは「入口」だけど、Childrenで渡されたものだけは、そのクライアントでは処理されない可能性がある、つまりuse clientを記述したコンポーネント自体はクライアントで処理されるがPropsとしてサーバーコンポーネントを渡すことができる点が、使いこなす上での大きなポイントですよね。

うひょ: そうですね。コンポーネントが「使われる」という概念には2種類あり、ちょっと理解を難しくしていますよね。コンポーネントの定義内で使うのか、Childrenとしてレンダリングするのかの深堀りは重要で、区別をつけられると良いでしょう。おそらくわかりやすい説明としては、インポートの関係で考えることです。use clientと書かれたファイルからインポートされたコンポーネントは、すべてクライアントコンポーネントとして扱われます。一方Childrenはインポートじゃなくて、別のところからPropsで渡されるので、必ずしもクライアントコンポーネントになるとは限りません。こうすると比較的分かりやすいんじゃないかと思います。

モデレーター: ありがとうございました。お二方のお悩み相談室はこちらで終了とさせていただきます。

アーカイブ動画・発表資料

イベント本編は、アーカイブ動画を公開しています。また、当日の発表資料も掲載しています。あわせてご覧ください。

▼動画・資料はこちら
フロントエンド大集合!うひょさん&あっきーさんと語る!Next.jsお悩み相談室
※動画の視聴にはFindyへのログインが必要です。

プロフィール