NestJS 12 ESM 전환과 Standard Schema — class-validator 탈출하기

Maru

@maru

NestJS 12 ESM 전환과 Standard Schema — class-validator 탈출하기

NestJS 12 ESM 전환과 Standard Schema — class-validator 탈출하기

NestJS 12는 프레임워크 전반을 ESM으로 전환하고 Standard Schema 1.0 규격을 공식 도입하며 대대적인 플랫폼 현대화에 나섰습니다. Node.js 20.19.0 또는 22.12.0 이상에서 제공하는 동기식 require(esm) 기술 덕분에, 기존 CommonJS 기반 프로젝트를 무너뜨리지 않고도 안전하고 점진적인 마이그레이션이 가능해졌습니다. 오랫동안 런타임 성능의 발목을 잡던 데코레이터 기반의 class-validator 의존성을 걷어내고 현대적인 스키마 검증이 선사하는 DX와 효율성을 경험할 때입니다.

모든 패키지의 ESM 전환과 require(esm)을 통한 호환성 보장

NestJS 12는 프레임워크 코어와 모든 공식 라이브러리를 완전한 ESM 패키지로 배포하며 대대적인 플랫폼 현대화를 시작합니다. 이 대전환을 안정적으로 지원하기 위해 필요한 최소 Node.js 요구 버전은 v20.19.0 또는 v22.12.0 이상으로 상향되었습니다.

가장 고무적인 부분은 하위 호환성을 지키는 방식입니다. NestJS 12는 Node.js가 자체적으로 제공하는 동기식 require(esm) 기술을 적극 활용합니다. 덕분에 기존 CommonJS 기반 애플리케이션에서도 코드베이스 전체를 당장 ESM으로 갈아엎지 않고, ESM 전용으로 배포된 NestJS 12 모듈을 에러 없이 그대로 임포트하여 사용할 수 있습니다. 프레임워크 업그레이드와 소스코드의 ESM 전환을 완전히 별개의 단계로 분리해 점진적으로 진행할 수 있어 마이그레이션 부담이 크게 줄어듭니다.

ESM 마이그레이션 실전: package.json 및 tsconfig.json 설정

NestJS 12로 완전한 ESM 환경을 구축하는 것은 설정 몇 줄만으로 가능할 만큼 간단합니다. 성공적인 마이그레이션을 위해서는 런타임인 Node.js 버전을 v20.19.0 또는 v22.12.0 이상으로 맞추고, 패키지와 컴파일러 설정을 명시적으로 조율해야 합니다.

가장 먼저 package.json 파일에 "type": "module"을 추가해 패키지 기본값 자체를 ESM으로 선언하고, tsconfig.json 파일의 모듈 옵션을 NodeNext로 일치시켜 줍니다.

json
// package.json (NestJS 12 및 Node.js v20.19+ 기준)
{
  "type": "module"
}

json
// tsconfig.json
{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

이 설정을 적용한 프로젝트에서는 Node.js ESM 표준 제약 조건에 따라 상대 경로 임포트 방식이 바뀝니다. 소스 파일이 실제로는 .ts 확장자로 저장되어 있더라도, 코드 안에서 다른 내부 모듈을 불러올 때는 반드시 .js 확장자를 명시해야 합니다. 예를 들어 import { AppService } from './app.service.js'처럼 파일명을 명확히 기재해야 컴파일과 런타임 에러를 방지할 수 있습니다.

Standard Schema v1.0 기본 탑재: Zod와 Valibot 직접 주입하기

기존 NestJS에서 입력값 검증을 위해 사용하던 class-validatorclass-transformer 조합은 무거운 런타임 메타데이터 리플렉션으로 인해 애플리케이션 성능과 번들 크기 면에서 큰 아쉬움이 있었습니다. NestJS 12는 이러한 고질적인 문제를 해결하기 위해 여러 스키마 검증 라이브러리를 아우르는 표준 규격인 Standard Schema 1.0을 네이티브로 도입했습니다. 이제 서드파티 래퍼 라이브러리 없이도 Zod, Valibot, ArkType 등으로 작성한 스키마를 라우트 핸들러에 곧바로 주입할 수 있습니다.

NestJS 12 환경에서는 @Body(), @Query(), @Param() 등의 기본 요청 데코레이터가 schema 매개변수 옵션을 직접 지원하도록 확장되었습니다. 아래는 Zod 스키마를 컨트롤러에 직접 연결하여 입력 데이터를 검증하는 간단하고 효율적인 예시입니다.

typescript
import { Controller, Post, Body } from '@nestjs/common';
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

type CreateUserDto = z.infer<typeof CreateUserSchema>;

@Controller('users')
export class UsersController {
  @Post()
  createUser(@Body({ schema: CreateUserSchema }) body: CreateUserDto) {
    return body;
  }
}

데코레이터에 스키마를 지정하면 프레임워크가 내부적으로 StandardSchemaValidationPipe를 동작시켜 요청 본문을 자동으로 검증합니다. 별도의 유효성 검증 파이프나 복잡한 커스텀 설정을 추가하지 않고도, 런타임 성능 오버헤드를 대폭 줄이면서 타입 컴파일 시점과 실행 시점 모두에서 강력한 타입 안전성을 달성할 수 있습니다.

그린필드 ESM 프로젝트를 위한 최신 고성능 도구의 기본 탑재

NestJS 12에서 CLI로 생성하는 신규 ESM 프로젝트는 현대적이고 성능이 뛰어난 개발 툴체인을 기본으로 채택합니다. 기존 Webpack 대신 Rust 기반의 초고속 번들러인 Rspack이 활성화되며, 테스팅 도구는 Jest에서 Vitest로 변경됩니다. 특히 이 환경의 Vitest는 OXC를 활용해 NestJS 데코레이터를 신속하게 파싱하므로 테스트 실행 속도가 크게 개선됩니다. 코드 린터 역시 무거운 ESLint 대신 가볍고 빠른 oxlint가 디폴트로 작동해 전체적인 피드백 루프가 빨라집니다.

반면 기존 CommonJS 환경을 유지해야 하는 프로젝트를 위해 하위 호환성도 철저히 보장합니다. CLI에서 CommonJS 템플릿을 생성할 때는 검증된 기존 도구인 Webpack, Jest, ESLint 조합이 그대로 유지됩니다. 급격한 도구 교체로 발생할 수 있는 빌드 에러나 설정 충돌 우려 없이, 각 팀의 템포에 맞춰 안전하게 마이그레이션을 진행할 수 있습니다.

안전하게 마이그레이션 로드맵 준비하기

NestJS 12가 제안하는 변화의 핵심은 하위 호환성을 완벽하게 보장하면서도 미래 지향적인 생태계로 나아갈 길을 열어두었다는 점입니다. 당장 소스코드 전체를 ESM으로 전환해야 한다는 부담을 가질 필요 없이, 패키지 버전만 먼저 올린 상태에서 기존 환경을 안정적으로 유지할 수 있습니다.

입력값 검증 역시 새롭게 설계하는 라우트부터 Standard Schema와 Zod를 결합해 점진적으로 전환하며 무겁고 느렸던 기존 class-validator 의존성을 단계적으로 걷어내는 전략을 추천합니다. 유연하고 차분한 로드맵을 구성해 더 가볍고 민첩해진 NestJS 생태계의 혜택을 안전하게 누려보시기 바랍니다.


참고 링크

(Edited)

No comments yet.