NestJS 12 Rspack移行 — モノレポのビルド速度を向上させる

Maru

@maru

NestJS 12 Rspack 마이그레이션 — 모노레포 빌드 속도 올리기

NestJS 12 Rspack移行 — モノレポのビルド速度を向上させる

NestJS 12は、公式CLIにおける従来のWebpackサポートを終了し、Rustベースの超高速バンドラーであるRspackを公式ビルドツールとして導入します。ビルド速度が大規模モノレポの開発生産性を左右する核心要素となった今、Rspackへの移行は非常に歓迎すべき変化です。しかし、既存のビルド体制を入れ替える過程で、SWCデコレータメタデータの欠落や、プロダクションビルドでのクラス名の難読化といった難解な技術的障壁に直面しがちです。NestJS 12ベースのモノレポ環境で、こうした現実的な移行問題を円滑に解決し、Rspack導入の効果を最大化するための実務ガイドをまとめました。

Rspack移行の利点:なぜWebpackを代替するのか

NestJS 12は、長らく公式CLIの中核を担ってきたWebpackサポートを終了し、Rustベースの超高速バンドラーであるRspackを公式ビルドツールとして採用しました。大規模なモノレポ環境において、Webpackの遅いコンパイルと重いホットリロードは、開発フローを分断する最大のボトルネックでした。Rspackは、Webpackのアーキテクチャおよび設定フレームワークとの優れた互換性を維持しながら、ビルド性能を圧倒的に引き上げることでこの問題を解決します。

Trilon Consultingの公式発表によると、NestJS 12はネイティブESMへの移行とともに、Vitest、oxlint、そしてRspackへと続く現代的で超高速なツールチェーンを提供します。この変化により、開発者は重いバックエンドのコードベースであっても、コールドスタートやソースコード変更時に発生する待機時間を大幅に短縮できます。既存のWebpackプラグインエコシステムの大部分をそのまま活用しながら、Rustバンドラーの圧倒的な性能を容易に確保できる点が、移行の最大の理由です。

核心的な障壁1:SWCデコレータメタデータの復元

Rspackはビルド速度を最大化するため、デフォルトでtscの代わりに内蔵されたSWCローダーであるbuiltin:swc-loaderを使用してTypeScriptコードをコンパイルします。この方式はビルド性能を飛躍的に向上させますが、NestJS環境ではランタイムエラーという致命的な障壁にぶつかります。NestJSの核心的なメカニズムである依存性の注入(Dependency Injection)が、デコレータメタデータに基づいて動作するためです。

SWCはデフォルトの状態ではコンパイル時にデコレータメタデータを生成しないため、明示的な設定を追加しない限り依存性の注入は完全に失敗します。この問題を解決するには、Rspack設定ファイルでSWCローダーのlegacyDecoratorとdecoratorMetadataオプションを必ず有効にする必要があります。

rspack.config.mjsファイルで以下のようにローダー設定をカスタマイズすることで、メタデータを完全に復元できます。

javascript
// rspack.config.mjs
export default {
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: { syntax: 'typescript', decorators: true },
              transform: { legacyDecorator: true, decoratorMetadata: true }
            }
          }
        }
      }
    ]
  }
};

この設定が適用されないと、アプリケーション起動時に依存関係を識別できず、ランタイムクラッシュが発生します。したがって、Rspack移行を開始する際に最も優先して動作を確認すべき必須チェックポイントです。

核心的な障壁2:プロダクションビルドにおけるクラス名の難読化の防止

Rspackでプロダクションビルドを行う際に最も注意すべき罠は、クラス名と関数名の難読化処理です。Rspackが提供するデフォルトのコード圧縮器は、ファイルサイズを最小化するために内部識別子をランダムな文字列に縮小します。しかし、NestJSは依存性の注入トークンを生成したり、実行コンテキストを照会するcontext.getClass()メソッドなどを実行する際、クラスの実際の名前と参照を識別子として頻繁に活用します。クラス名が任意に変更されると、ランタイムで必要なガードやサービスインスタンスを正常に特定できず、異常なルーティング遮断や依存関係解決の失敗エラーに直面することになります。

この誤動作を根本的に防ぐには、ビルドツールチェーンがクラスと関数の元の名前を保持するように措置を講じる必要があります。Rspackの内蔵JavaScript圧縮プラグインであるSwcJsMinimizerRspackPluginの最適化設定においてcompressとmangleルールを手動で編集しkeep_classnamesおよびkeep_fnamesプロパティを有効にすれば問題ありません。

以下は、Rspack設定を通じてNestJSアプリケーションのプロダクション安定性を確保するための最適化例です。

javascript
// rspack.config.mjs (Rspack 1.x 및 NestJS 12 기준)
import { rspack } from '@rspack/core';

export default {
  optimization: {
    minimizer: [
      new rspack.SwcJsMinimizerRspackPlugin({
        minimizerOptions: {
          compress: { keep_classnames: true, keep_fnames: true },
          mangle: { keep_classnames: true, keep_fnames: true }
        }
      })
    ]
  }
};

この設定を追加すると、コード圧縮を行うコンパイラが元のクラス名の構造を安全に維持します。これにより、プロダクションへのデプロイ後もNestJSエンジンのメタデータマッピングフローが崩れることなく、完全に一貫性を保つことができます。

核心的な障壁3:HMRソケット衝突とEADDRINUSEエラー

Rspackの圧倒的なビルド速度を開発サーバーで享受するにはホットリロードの有効化が不可欠ですが、これをバックエンドに適用するとポート占有エラーという伏兵に遭遇します。コードが変更されるたびにプロセスは維持されたままモジュールだけが入れ替わるHMRの特性上、以前のアプリケーションインスタンスが使用していたネットワークソケットが正常に閉じられず、EADDRINUSEエラーを引き起こす現象です。

これを完全に防ぐには、NestJSのアプリケーションライフサイクル管理機能を利用して、モジュールが入れ替わる直前に既存インスタンスを明示的に解放する必要があります。Rspack環境で@rspack/core/hot/poll?100とrun-script-webpack-pluginを組み合わせてHMR環境を構築した場合、メインエントリーポイントファイルであるmain.tsにライフサイクルクリーンアップロジックを必ず挿入しなければなりません。

開発環境でソケット衝突を起こさずにバックエンドインスタンスを安全に再起動するためのmain.ts設定は以下の通りです。

typescript
// main.ts (NestJS 12 / Rspack HMR 대응)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

declare const module: any;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => app.close());
  }
}
bootstrap();

このようにmodule.hot.disposeコールバック内部でapp.close()を呼び出しておけば、新しいコードが反映される前に有効だったHTTPサーバーソケットが確実に整理され、スムーズで高速なホットリロード環境を完成させることができます。

NxモノレポでのRspackカスタムアダプター構成

Nxモノレポ環境で提供される@nx/rspack:convert-webpackのような自動変換ツールは、バックエンドのNodeアプリケーションのWebpack設定を移行する際に予期せぬ欠落を頻繁に引き起こします。フロントエンドのビルドとは異なり、バックエンドのビルドはファイルシステムアクセスやネイティブモジュールのバインディングなど、Nodeランタイムの特性を考慮する必要があるため、自動変換ツールだけに頼るよりも手動でカスタム設定を構成する方がはるかに安定しています。

手動設定の肝は、ビルド対象プラットフォームをNodeと明示し、外部依存関係がバンドル結果に丸ごと同梱される問題を防止することです。そのためにアプリケーションルートにrspack.config.mjsファイルを作成し、nodeExternalsを適用してnode_modulesフォルダ内のパッケージをバンドル対象から除外する必要があります。

Nx環境で動作するNestJS用カスタムRspack設定の例です。Nx 19以上のバージョンを基準に作成されています。

javascript
import { composePlugins, withNx } from '@nx/rspack';
import nodeExternals from 'webpack-node-externals';

export default composePlugins(withNx(), (config) => {
  config.target = 'node';
  config.externalsPreset = { node: true };
  config.externals = [
    nodeExternals({
      allowlist: [/^@monorepo\//] 
    })
  ];
  return config;
});

このようにカスタムアダプターを構成すれば、不要なサードパーティライブラリがバンドルに含まれる問題を確実に遮断できます。結果としてモノレポ内の共有ライブラリだけが安全にコンパイルに含まれるため、ビルド速度が劇的に改善され、プロダクション環境のイメージサイズも大幅に最適化されます。

Rspack導入、どのように準備すべきか

NestJS 12とRspackの組み合わせは、大規模なモノレポを運用する開発チームにとって、ビルド速度の停滞を解消するための確実な突破口です。現在提供されている@nestjs/cli@nextプレリリースパッケージを通じてあらかじめ移行を検討し、先述のデコレータメタデータとクラス名維持の設定を確認することをお勧めします。プロダクションビルドの安定性とローカル開発環境を事前に検証しておけば、今後の公式リリース時に大きな混乱なく、圧倒的なビルド速度の向上を享受できるでしょう。


参考リンク

まだコメントはありません。