■ 1. 記事の背景と目的
- カミナシエンジニアのosuzuによる技術記事である
- 職能柄Webフロントエンド技術の選定に関わる機会が多くReact Server ComponentやNext.jsに関する発信を過去にしていた
- 2025年12月のReactやNext.jsのセキュリティ問題に対し心痛めている
- 現在もプロダクションでNext.jsを運用しているが選定した事を後悔しているかというとそう単純な話でもない
- あらためてNext.jsをプロダクションで採用するポイントや何を意識した構成にしているか記載する
- 以降ReactはRSC(React Server Components)を含むものとしNext.jsは記載のない限りApp Routerを指した話となる
■ 2. BFFとしての使用方針
- プロダクション運用するプロダクトに対してフルスタックフレームワークやバックエンドを兼ねる形でNext.jsを採用する事はない
- 必ずBFFとして使う
- 主にSaaSのような認証認可が必要かつマルチプロダクト展開や他SaaS連携などで複数のAPIに依存しやすい構成を例に説明する
■ 3. BFFの構成設計
- 推奨する「Next.jsをBFFとして使う」構成は物理的にも論理的にもバックエンドAPIと明確に分離された状態を指す
- Next.jsはデータベースへの接続情報を一切持たずビジネスロジックも記述しない
- あくまで「UIのためのデータ整形」「APIアグリゲーション」「API認可プロキシ」に徹する
■ 4. セキュリティ設計思想
- Next.jsサーバー(Node.js/Edge Runtime)を「信頼できるサーバー(Trusted)」とは見なさず「ブラウザが少し権限を持った延長線上の環境(Untrusted)」として扱う
- 「ブラウザのDevToolsで見えて困るロジックや変数はNext.jsのサーバーサイドにも置かない」という極端な意識で設計する
- DBのパスワードやAWSのシークレットキーを環境変数として渡すことはない
- 万が一RSCの境界設定ミスでコードが流出したりインジェクション攻撃を受けたとしても攻撃者が手に入れられるのは「画面構築のためのロジック」だけであり系統全体を掌握されるリスクを構造的に低減する
■ 5. インフラ権限の最小化
- BFFが実行されるコンテナや関数自体の権限(IAM Role等)は最小限に絞る
- AWS上で運用する場合Next.jsの実行ロールに対してdynamodb:FullAccessは与えずにdynamodb:GetItemやdynamodb:PutItemなど権限を最小限にする
- 認証認可のフローにおいてもNext.jsはあくまでトークンの「運搬役」に徹する
- Next.js上でJWTを発行するといった構成は取らない
■ 6. 認証認可の責務分離
- カミナシが運用するプロダクトではバックエンドAPIはOAuthのクライアントでトークンイントロスペクションのリクエストするだけという構成を取っている
- Next.jsはCookieからトークンを取り出しAPIへ転送するだけでありバックエンドAPI側がそのトークンを検証するリクエスト結果を元に認可する
- この徹底した責務の分離によりBFF層がセキュリティに対して権限やリスク集中することを防いでいる
- すべてのバックエンドAPIが同じセッションストアを共有する構成などではBFFでトークンを取得せずcookieをproxyするだけで扱って良いかもしれない
■ 7. BFFの必要性の議論
- 「そこまで制約を課すならSPA + APIで良いのではないか」という議論がある
- SaaSやtoBのプロダクトではSPA(Client Side Rendering)にすべきという意見もよく目にする
- 「SSRからSPAにしたとしてSaaSのようなプロダクトではボトルネックは移動しない」「静的なファイルとして配信した方がインフラコストが安いまたバンドルしたファイルもユーザーのブラウザでキャッシュ可能」など
- 上記にも賛同するし前職でそう構築した経験もある
- 残念ながら何事にもトレードオフがあるため主にBFFを置くこと自体のメリットを記載する
■ 8. BFFを置くメリット
- メリットは「認可の橋渡し(Token Handler)」と「APIアクセスの集約」にある
- 現代のバックエンド(特にマイクロサービス構成)はステートレスなBearer Tokenを要求することが一般的である
- SPA単体でこれを実現しようとするとJSでトークンを扱う(XSSリスク)かバックエンド側すべてにCookie認証を対応させる(実装コスト増・CORS問題)必要がある
- BFFを挟むことで「ブラウザとはCookie(セッション)で通信しバックエンドとはTokenで通信する」という構成が素直に取りやすい
- Token Handlerのためにサイドカーや軽量プロキシを扱うことも可能ではある
- APIアクセスの集約(APIアグリゲーション)に関してはコード的な問題だけでなくAPIが同一VPCのinternalなリクエストで通信出来たりAPIとBFFを同じリージョンにまとめたりとネットワークの効率が優れている
- Reactチームはブラウザから大量の遅いリクエストが飛んでしまう問題をBoomer Fetchingとして問題視している
■ 9. RSCのBFFとしての優位性
- BFFの中であえてNext.jsを選ぶ理由はRSCがもたらす開発体験とBFFとしての適性にある
- RSCを「宣言的なBFF」として捉えると非常に優秀である
- 従来BFF層でAPIを叩くためにはExpressを使ったりGraphQLを使うなどの詰め替え業が必要だった
- RSCであればコンポーネントツリーの中で直接非同期データを取得しそれをPropsとして流し込むだけで済む
- データの取得とUIの構造がコロケーション(同居)されかつ型安全にバックエンドAPIのレスポンスをクライアントコンポーネントへ渡せる
- RSCによってReactを同期的に書くことができて非同期で発生する難しさをいくつも解消してくれる
■ 10. Next.jsの課題点
- 発明を捨てフルスタックフレームワークとしての道を失った
- Next.jsはApp Router以降フルスタックフレームワークとしての道を失ったと思っている
- Page RouterまではServerからClientへのPropsの境界が明確で(いわゆるgetServerSidePropsで境界が分かれてた)貧者の選択としてNext.jsでサーバーを実装する選択も取り得た
- 今回のCVE以降は特にそうした構成を取る事は明確に難しくなる
■ 11. 代替フレームワークの出現
- Tanstack Startというフレームワークが対抗として出現している
- UIライブラリがReactに依存していなかったり(これはTanstack系は共通の思想)Reactに非依存な層を増やしたい人には嬉しい
- サーバー/クライアントの境界を明確に区別しようという思想もありこうしたコンセプトもNext.jsより共感する人が多いと思う
- 紹介したTanstack Routerも他に有力なReact Routerも将来的にRSCを取り入れていく方針である
- 今の上記フレームワークのシンプルさはRSCに対応してないゆえでもあり今後どの程度混乱が生じるかは分からない
- それならいっそReact以外という道を考える人もいるかもしれない
- 採用市場やフレームワーク自体の完成度を見るに難しい選択にはなると思う
■ 12. 技術選定に対する考え方
- 今フロントエンドでフレームワークやライブラリを選定することは考慮すべきポイントが多くて難しい時代だと思う
- きっと色んな言葉が気になってしまうエンジニアも多いと思う
- 近年大切にしてるテーマの一つに「どうでもいいことは流行に従い重大なことは標準に従いドメインのことは自ら設計する」という言葉がある
- カミナシという組織でSaaSを開発しているがカミナシには「現場ドリブン」というドメインに時間を使おうという概念そのままのバリューが存在する
- 2025年だけでCS活動やプロダクトディスカバリや商談同席など現場に10件以上訪問して課題に向き合ってきた
- エンジニアとしての時間をドメインを考えることに対して使っていきたいという想いが年々強まっている
- 技術選定の正解探しに時間を溶かすのではなく選んだ技術を正解にするためのオーナーシップと何よりユーザーへの価値提供を大切にしていきたい