TypeScript 5.0의 Decorator 메타데이터 적용기
배경
NestJS 프로젝트에서 계속 experimentalDecorators: true 플래그를 사용해왔는데, TypeScript 5.0부터 ECMAScript 표준 Decorator가 정식 지원되면서 마이그레이션을 검토하게 되었다.
기존에는 tsconfig.json에 실험적 플래그를 유지해야 했다.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
주요 변경사항
가장 큰 차이는 Decorator의 실행 시점과 타입 정의였다. 기존 experimental decorator는 any 타입을 많이 사용했지만, 새 표준은 명확한 타입을 요구한다.
// 기존 방식
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey}`);
return original.apply(this, args);
};
}
// 표준 방식
function Log(target: Function, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
return function(this: any, ...args: any[]) {
console.log(`Calling ${methodName}`);
return target.apply(this, args);
};
}
실제 문제
NestJS의 @Injectable(), @Controller() 같은 데코레이터들은 여전히 experimental 방식을 사용하고 있어서 전면 마이그레이션은 불가능했다. 프레임워크가 업데이트되지 않은 상태에서 일부만 전환하면 타입 충돌이 발생했다.
결국 커스텀 데코레이터만 선택적으로 표준 방식을 적용하고, 프레임워크 데코레이터는 기존 방식을 유지하는 하이브리드 접근을 택했다. TypeScript 5.0은 두 방식을 동시에 지원하므로 큰 문제는 없었다.
결론
표준 Decorator는 타입 안정성이 개선되었지만, 생태계가 따라오지 못한 상태에서는 섣불리 전환하기 어렵다. 당분간은 두 방식을 혼용하다가 NestJS 10.x 버전에서 공식 지원이 나오면 그때 본격적으로 마이그레이션할 계획이다.