@maru

NestJS 12 프리뷰 — class-validator 버리고 Zod 바로 쓴다
NestJS 12 프리뷰는 백엔드 개발을 번거롭게 만들던 레거시 의존성과 복잡한 빌드 설정을 대대적으로 개선하는 메이저 업데이트입니다. 코어 수준에서 Standard Schema 1.0을 지원해 class-validator 없이도 Zod나 Valibot으로 요청 데이터를 직접 검증할 수 있게 되었고, 전체 패키지가 네이티브 ESM으로 전환됩니다. 여기에 Rspack과 Vitest 같은 Rust 기반 고성능 툴체인을 기본으로 도입하면서 개발 생산성도 대폭 향상되었습니다. 백엔드 개발자의 관점에서 이번 메이저 업데이트가 어떤 문제를 해결하고 있으며, 안전하게 마이그레이션하기 위해 무엇을 준비해야 하는지 하나씩 짚어보겠습니다.
Standard Schema 1.0 도입: class-validator와의 작별
NestJS 12의 가장 큰 변화 중 하나는 Standard Schema 1.0 규격을 프레임워크 코어에 네이티브로 도입했다는 점입니다. 이제 라우트 핸들러의 @Body(), @Query(), @Param() 데코레이터에 Zod나 Valibot 같은 스키마를 직접 인자로 넘길 수 있습니다.
과거 NestJS에서 페이로드를 검증하려면 class-validator와 class-transformer에 의존해야 했습니다. 이 방식은 데코레이터 기반으로 동적 타입을 추론하기 때문에 tsconfig.json에서 emitDecoratorMetadata 옵션을 반드시 활성화해야 했습니다. 하지만 이 옵션은 TypeScript 컴파일 타임에 무거운 연산을 요구할 뿐만 아니라 Rspack, Vite, esbuild 같은 러스트 기반 초고속 번들러들과 호환되지 않아 모던 빌드 도구 도입의 가장 큰 걸림돌이었습니다.
NestJS 12는 이 문제를 해결하기 위해 내장된 StandardSchemaValidationPipe를 제공합니다. 런타임에 메타데이터 리플렉션에 의존하지 않고, 데코레이터에 직접 주입된 스키마 객체의 표준 검증 메서드를 파이프 내부에서 바로 실행하는 구조입니다. 덕분에 번거로운 컴파일러 옵션 설정이나 런타임 리플렉션 오버헤드 없이 안전하고 빠른 검증 파이프라인을 구축할 수 있습니다.
기존 class-validator 방식과 NestJS 12의 Zod 기반 검증 방식을 비교하면 차이가 명확히 드러납니다.
// [기존 방식] class-validator 사용 (emitDecoratorMetadata 옵션 필수)
import { IsString, IsEmail } from 'class-validator';
export class CreateUserDto {
@IsString()
name!: string;
@IsEmail()
email!: string;
}
@Post()
create(@Body() createUserDto: CreateUserDto) {}// [NestJS 12 방식] Standard Schema 사용 (Zod 스키마 직접 주입)
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
@Post()
create(@Body({ schema: CreateUserSchema }) body: z.infer<typeof CreateUserSchema>) {}이처럼 스키마를 컨트롤러 데코레이터에 직접 넘기기만 하면 내부적으로 StandardSchemaValidationPipe가 페이로드를 검증하고, 필요 시 StandardSchemaSerializerInterceptor가 응답 직렬화까지 처리합니다. 복잡한 외부 통합 라이브러리 없이 표준 스키마 사양을 그대로 사용할 수 있어 개발자 경험과 빌드 속도가 모두 크게 향상됩니다.
네이티브 ESM 전환과 동기식 require(esm) 호환성
NestJS 12의 모든 코어 패키지는 네이티브 ESM으로 전환됩니다. 그동안 백엔드 생태계에서 CommonJS와 ESM 사이의 모듈 임포트 설정은 개발자의 시간을 가장 많이 앗아가는 고질적인 문제였습니다. 이번 메이저 업데이트는 이 장벽을 허물기 위해 최신 Node.js 환경의 동기식 require(esm) 기능을 적극적으로 활용합니다.
동기식 require(esm)은 Node.js v20.19.0 이상 및 v22.12.0 이상 버전에서 공식 지원되는 핵심 기능입니다. 이 기능 덕분에 기존에 CommonJS 기반으로 작성된 레거시 애플리케이션 설정을 당장 완전히 바꾸지 않고도, 네이티브 ESM으로 빌드된 NestJS 12 코어 패키지를 안전하게 불러와 호환성을 유지할 수 있습니다.
실제 커뮤니티 벤치마크 결과를 살펴보면, NestJS 11 기반의 CJS 환경과 NestJS 12 네이티브 ESM 환경 사이에 콜드 스타트 시간이나 유휴 상태의 메모리 점유율(RSS) 차이는 거의 미미한 수준입니다. 결국 이번 ESM 전환의 진짜 가치는 당장의 드라마틱한 성능 향상보다는, 현대적인 ESM 전용 라이브러리들을 까다로운 설정이나 별도 래퍼 없이 즉시 프로젝트에 통합할 수 있는 개발 경험의 개선에 있습니다.
Rspack과 Vitest, oxlint를 아우르는 초고속 Rust 툴체인
NestJS 12는 개발 피드백 루프를 극적으로 단축하기 위해 Rust 기반의 현대적인 개발 도구를 대거 채택했습니다. 이 새로운 빌드 및 테스트 시스템은 CLI 명령어로 생성하는 신규 네이티브 ESM 프로젝트 템플릿에 기본으로 적용됩니다. 기존 CommonJS 프로젝트와의 생태계 안정성을 고려하여, CJS 기반 템플릿은 기존 Webpack과 Jest, ESLint 구성을 그대로 유지합니다.
가장 먼저 눈에 띄는 변화는 오랜 기간 기본 번들러로 활약한 Webpack이 Rust 기반의 초고속 번들러인 Rspack으로 대체되었다는 점입니다. 빌드 성능이 수배 이상 빨라져 로컬 개발 서버 기동과 프로덕션 빌드 속도가 획기적으로 개선됩니다. 여기에 코드 분석 도구 역시 무거운 ESLint 대신 Rust로 작성되어 압도적인 파싱 속도를 자랑하는 oxlint를 채택하여 저장 시 코드 분석 대기 시간을 최소화했습니다.
테스트 환경에도 큰 변화가 있습니다. ESM 환경에서 설정이 까다롭고 무거웠던 Jest 대신 Vitest가 새로운 기본 테스트 러너로 들어옵니다. 특히 이 Vitest는 초고속 구문 분석 엔진인 OXC를 탑재하여 NestJS의 복잡한 데코레이터를 신속하게 처리합니다. 실제 마이그레이션 사례에 따르면 기존에 42초가 소요되던 전체 테스트 실행 시간이 단 11초로 감소하는 등, 개발자가 로컬과 CI/CD 파이프라인에서 느끼는 대기 시간이 압도적으로 줄어듭니다.
안전한 마이그레이션을 위해 지금 준비해야 할 일
NestJS 12 마이그레이션을 안정적으로 진행하려면 런타임 환경과 타입스크립트 설정을 미리 조율해 두어야 합니다.
가장 먼저 서버가 구동되는 Node.js 버전을 업데이트해야 합니다. 코어 패키지가 네이티브 ESM으로 전환되더라도 기존 CommonJS 프로젝트와의 호환성을 무너뜨리지 않으려면, 동기식 require(esm) 기능을 정식 지원하는 Node.js v20.19.0 이상 또는 v22.12.0 이상 환경이 반드시 확보되어야 합니다.
그 다음 단계로 타입스크립트 컴파일러 설정을 변경합니다. tsconfig.json 파일에서 module과 moduleResolution 옵션을 모두 NodeNext로 업데이트하여 ESM 모듈 탐색 규칙을 명확히 정의해야 합니다.
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}NodeNext 규격을 적용하면 로컬 파일을 상대 경로로 가져오는 모든 import 구문에 .js 확장자를 필수적으로 명시해야 합니다. 실제 개발 소스 파일이 .ts 확장자로 작성되어 있더라도, 빌드 후 실행되는 자바스크립트 결과물을 기준으로 경로를 해석하기 때문에 사전에 임포트 구문을 전수 검토하고 확장자를 추가해 두는 것이 좋습니다.
결론: 마이그레이션, 지금 바로 적용할 것인가
NestJS 12는 프레임워크 발목을 잡던 오래된 기술 부채를 정리하고 생태계 현대화를 이끄는 중요한 업데이트입니다. 새로 시작하는 프로젝트이거나 이미 Zod나 ESM 패키지를 적극적으로 사용하고 있다면 도입할 가치가 충분합니다. Rust 기반의 새로운 개발 툴체인과 네이티브 ESM의 편리함을 즉시 누릴 수 있기 때문입니다.
다만 기존 서비스를 운영 중이라면 서두를 필요는 없습니다. 실제 벤치마크 결과에 따르면 ESM 전환이 가져오는 극적인 런타임 성능 향상이나 메모리 절감 효과는 미미합니다. 따라서 성능 개선을 목적으로 무리하게 업그레이드를 감행하기보다는, class-validator 기반 코드를 점진적으로 표준 스키마로 전환하고 사용 중인 라이브러리의 ESM 지원 여부를 검토하며 차근차근 진행하는 것이 현명한 마이그레이션 전략입니다.
참고 링크