BLOG

ブログ

IISでApp RouterのNext.jsを動かした時、loading.tsxが動かなかった話

はじめに

今回はちょっとニッチな記事です!

現在はクラウド全盛期ということもあるのであまり多い構成とは言えませんが、様々な理由でクラウドでサービス展開をせず、オンプレミスで環境を作るWebシステムも存在します。

そうした環境で使用されることが多いのがIIS(Internet Information Services)です。IISはWindows Server用のWebサーバーソフトウェアで、WebサイトやWebアプリケーションの公開や管理に用いられます。

誤解を恐れず簡略的にいうとWindowsサーバを外部公開するための設定をGUIで比較的簡単に設定できるソフトウェアです。同じWebサーバのソフトとしてはApacheやNginxが挙げられます。

また、Next.jsのApp Routerには、ルーティングによるページ遷移やデータフェッチ中にローディングUIを表示する機能があります。具体的には、loading.tsxというファイルを作成することで、該当ルートセグメントのデータフェッチやレンダリングが完了するまでの間、そのファイルに記述された内容を画面に表示することができます。

今回の内容はIIS環境でNext.jsを動かした時に、loading.tsxが動かなかった!という事象を解決する記事となります。

少しでも参考にしていただければ幸いです。それでは参ります。

技術構成

  • Windows
  • IIS(HttpPratformHanderでnodeを起動)
  • Next 14 (App Router)

結論

いきなり結論ですが、「router.pushをNext標準の<Link>に置き換える」ことで今回は解決することができました。

なぜこのような解決方法となったか、原因となる要素を順を追って解説していきます。

IISを経由するという構成

今回の構成の特徴として、ひとつめの原因として考えられるのが、IISがクライアントとNext.jsのアプリケーションを仲介するという構成上の問題です。ここでいうクライアントはアプリケーションを画面から操作するユーザー側を指します。

検証をしている中で、今回の事象は「ローカルの開発環境だと発生しない」という特徴がありました。npm run dev や npm run start で起動したNext.jsのアプリケーションは、クライアントのリクエストをNode.js環境上のアプリケーションが直接受け取って処理します。

しかし、IISで動かす場合、クライアントのリクエストを一度IISが受け取り、IISがアプリケーションにリクエストを伝える、という構成になります。

簡易的に表現すると、IISというフィルターがかかる構成、とも言えます。

基本的にIISは透過的に動作するはずですが、今回の件では、IISがリバースプロキシとしてリクエストやレスポンスを仲介する過程で、特定のHTTPヘッダーやストリームの処理方法に差異が生じ、それが事象を引き起こしたと考えられます。

<Link>とrouter.pushの違い

ふたつ目の原因としては、Next.jsにおけるルーティング方法の違いです。

<Link>とrouter.pushでは、内部のネットワークコネクションの仕方が異なったり、aタグの生成の有無など、Next.jsの内部仕様として、細かい違いがあります。

この辺りは内部のコードを読み尽くしたわけではないので、具体性を持って論じることはできませんが、さまざまな方がブログ等の発信で2つの違いを解説しています。

基本的にNext.js公式としては<Link>の使用を推奨しており、router.pushはページリダイレクトなど、関数の中で使用されることが想定されているようです。

上記二つの特徴が複合的な原因となった可能性

今回の事象は上記二つの要因が複合的に関わり合い発生したと推察しています。

IISではなく、ほかのWebサーバソフトであれば正常に動く可能性もありますし、同じことやもっと複雑な事象が発生するかもしれません。

Next.jsのルーティングについても、開発側の意図しない使用をするとこのような事象に繋がることがあります。

今回で言うと着眼点は下記の2点にまとめられます。

  • 構成の問題
  • ルーティングの仕様問題

別のフレームワークやソフトウェアを使用していても、似たような事象が発生した場合は上記の観点が役に立つかもしれません。

そのほかの解決策として

今回は<Link>で解決できましたが、全てのパターンに当てはまる解決方法ではありません。

router.pushを使用しなければ実装できないと言う問題もあるかもしれません。そのため、下記に別のアプローチ方法を紹介します。

詳細は今回割愛しますが、もしもの時の参考にしていただけると嬉しいです。

  • IISの構成をARR(Application Request Routing)を使用したリバースプロキシ構成に変更する(特に、ストリーミングレスポンスの最適化のためResponse buffer thresholdを0に設定する)
  • サーバーコンポーネントのfetchをクライアントコンポーネントでのfetchに移行する

まとめ

今回の記事では、Next.jsのApp Routerにおけるloading.tsxが、IISを経由したデプロイ環境で正常に動作しないという、ややニッチながらも特定の状況下で直面しうる課題について深掘りしました。

問題の核心は、主に以下の2点に集約されます。

  • IISのリバースプロキシとしての特性: クライアントとNext.jsアプリケーションの間でIISが仲介することで、リクエストやレスポンスの解釈に差異が生じ、特にストリーミングのような挙動が求められるloading.tsxの表示に影響を与えた可能性。
  • Next.jsのルーティング仕様: router.pushと<Link>では内部的なネットワークコネクションの方法が異なり、Next.jsが推奨する<Link>の使用が今回の解決策となりました。

最終的に、router.pushをNext.js標準の<Link>コンポーネントに置き換えることで事象は解決しましたが、これは全てのパターンに当てはまるわけではありません。記事後半で触れたように、IISの構成をARR(Application Request Routing)を使用したリバースプロキシ構成に変更することや、サーバーコンポーネントでのデータフェッチをクライアントコンポーネントに移行するといったアプローチも、状況によっては有効な解決策となり得ます。

クラウドが主流となる現代においても、オンプレミス環境や特定のレガシーなインフラとの連携が必要となる場面は存在します。そのような環境でNext.jsのようなモダンなフレームワークを導入する際には、今回の記事で取り上げたようなデプロイ環境とフレームワークの挙動の相互作用に注意を払うことが非常に重要です。

本記事が、同様の課題に直面している方々、またはIIS環境でのNext.jsデプロイに関心のある方々にとって、少しでも解決の糸口や新たな知見となれば幸いです。

それではまた!