@maru

NestJS 12 Rspack 마이그레이션 — 모노레포 빌드 속도 올리기
NestJS 12가 공식 CLI에서 기존 웹팩 지원을 중단하고 Rust 기반의 초고속 번들러인 Rspack을 공식 빌드 도구로 도입합니다. 빌드 속도가 대규모 모노레포의 개발 생산성을 결정짓는 핵심 요소가 된 지금, Rspack으로의 전환은 대단히 반가운 변화입니다. 하지만 기존 빌드 체계를 교체하는 과정에서 SWC 데코레이터 메타데이터 누락이나 프로덕션 빌드의 클래스명 난독화 같은 까다로운 기술 장벽에 부딪히기 쉽습니다. NestJS 12 기반 모노레포 환경에서 이러한 현실적인 마이그레이션 문제를 매끄럽게 해결하고, Rspack 도입 효과를 극대화할 수 있는 실무 실전 가이드를 정리했습니다.
Rspack 전환의 이점: 왜 Webpack을 대체하는가
NestJS 12는 오랫동안 공식 CLI의 중심을 지켰던 웹팩 지원을 중단하고 Rust 기반의 초고속 번들러인 Rspack을 공식 빌드 도구로 채택했습니다. 대규모 모노레포 환경에서 웹팩의 느린 컴파일과 무거운 핫 리로드는 개발 흐름을 끊는 가장 큰 병목이었습니다. Rspack은 웹팩 아키텍처 및 설정 프레임워크와 뛰어난 호환성을 유지하면서도 빌드 성능을 압도적으로 끌어올려 이 문제를 해결합니다.
Trilon Consulting의 공식 발표에 따르면 NestJS 12는 네이티브 ESM 전환과 함께 Vitest, oxlint, 그리고 Rspack으로 이어지는 현대적인 초고속 도구 체인을 제공합니다. 이 변화 덕분에 개발자는 무거운 백엔드 코드베이스에서도 콜드 스타트와 소스 코드 변경 시 발생하는 대기 시간을 대폭 줄일 수 있습니다. 기존 웹팩 플러그인 생태계를 대부분 그대로 활용하면서 Rust 번들러의 압도적인 성능을 손쉽게 확보할 수 있다는 점이 마이그레이션의 가장 큰 이유입니다.
핵심 장벽 1: SWC 데코레이터 메타데이터 복원
Rspack은 빌드 속도를 극대화하기 위해 기본적으로 tsc 대신 내장된 SWC 로더인 builtin:swc-loader를 사용하여 TypeScript 코드를 컴파일합니다. 이 방식은 빌드 성능을 비약적으로 끌어올리지만, NestJS 환경에서는 런타임 오류라는 치명적인 장벽을 만나게 됩니다. NestJS의 핵심 메커니즘인 의존성 주입이 데코레이터 메타데이터를 기반으로 동작하기 때문입니다.
SWC는 기본 상태에서 컴파일 시점에 데코레이터 메타데이터를 생성하지 않으므로, 명시적인 설정을 추가하지 않으면 의존성 주입이 완전히 실패합니다. 이 문제를 해결하려면 Rspack 설정 파일에서 SWC 로더의 legacyDecorator와 decoratorMetadata 옵션을 반드시 활성화해야 합니다.
rspack.config.mjs 파일에서 다음과 같이 로더 설정을 커스텀하여 메타데이터를 완벽하게 복원할 수 있습니다.
// 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의 내장 자바스크립트 압축 플러그인인 SwcJsMinimizerRspackPlugin의 최적화 설정에서 compress와 mangle 규칙을 수동으로 편집하여 keep_classnames 및 keep_fnames 속성을 활성화해 주면 됩니다.
다음은 Rspack 설정을 통해 NestJS 애플리케이션의 프로덕션 안정성을 확보하는 최적화 예시입니다.
// 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 설정은 다음과 같습니다.
// 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 같은 자동 전환 도구는 백엔드 노드 애플리케이션의 웹팩 설정을 마이그레이션할 때 예기치 못한 누락을 자주 일으킵니다. 프론트엔드 빌드와 달리 백엔드 빌드는 파일 시스템 접근이나 네이티브 모듈 바인딩 등 노드 런타임의 특성을 고려해야 하므로 자동 변환 도구에만 의존하기보다 수동으로 커스텀 설정을 구성하는 편이 훨씬 안정적입니다.
수동 설정의 핵심은 빌드 대상 플랫폼을 노드로 명시하고 외부 의존성이 번들 결과물에 통째로 묶이는 문제를 방지하는 것입니다. 이를 위해 애플리케이션 루트에 rspack.config.mjs 파일을 생성하고 nodeExternals를 적용하여 node_modules 폴더의 패키지들을 번들링 대상에서 제외해야 합니다.
Nx 환경에서 동작하는 NestJS용 커스텀 Rspack 설정 예시입니다. Nx 19 이상 버전을 기준으로 작성되었습니다.
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 프리릴리즈 패키지를 통해 미리 마이그레이션을 검토하고, 앞서 다룬 데코레이터 메타데이터와 클래스명 유지 설정을 점검해 보시길 권장합니다. 프로덕션 빌드의 안정성과 로컬 개발 경험을 미리 검증해 둔다면, 다가오는 공식 릴리즈 시점에 별다른 혼선 없이 압도적인 빌드 속도 개선을 누릴 수 있습니다.
참고 링크