Node.js, 이제 TypeScript를 기본 지원한다!

Node.js, 이제 TypeScript를 기본 지원한다!

 

Node.js가 v23.6.0부터 TypeScript를 별도의 플래그 없이 실행할 수 있도록 지원하는데요.


이 글에서는 새로운 기능이 어떻게 동작하는지, 그리고 주의해야 할 점들을 살펴보겠습니다.


새로운 기능, 직접 확인해볼까요?

다음과 같은 TypeScript 파일이 있다고 가정해봅시다.

// demo.mts
function main(message: string): void {
  console.log('Message: ' + message);
}
main('Hello!');

 

이제 이 파일을 바로 실행할 수 있습니다.

node demo.mts

 

하지만 현재는 다음과 같은 경고가 표시되는데요.

ExperimentalWarning: Type Stripping is an experimental feature and might change at any time

 

이 경고를 비활성화하려면 다음과 같이 실행하면 됩니다.

node --disable-warning=ExperimentalWarning demo.mts

 --disable-warning 사용 팁

  • 환경 변수 NODE_OPTIONS를 설정하면 매번 입력하지 않아도 됩니다.
  • .env 파일을 활용하면 환경 변수를 영구적으로 설정할 수도 있습니다.

파일 확장자는 어떻게 사용해야 할까요?

TypeScript 파일 확장자는 Node.js에서 다음과 같이 동작합니다.

확장자 설명
.ts .js 파일과 동일하게, ESM 또는 CommonJS 가능
.mts 항상 ESM으로 동작 (독립 실행 파일에 적합)
.cts 항상 CommonJS로 동작
.tsx 지원되지 않음 (JSX 불가)

 Node.js TypeScript vs 일반 TypeScript

JavaScript가 아닌 기능은 지원되지 않습니다

  • enum
  • namespace
  • 클래스 생성자의 매개변수 속성(parameter properties)

JSX는 지원되지 않습니다

  • .tsx 파일과 JSX 문법은 사용할 수 없습니다.

최신 JavaScript 기능을 변환하지 않습니다

TypeScript는 최신 JavaScript 기능을 변환(transpile)해주는 역할도 하는데요.


예를 들어, 데코레이터(Decorators) 같은 기능은 TypeScript에서는 지원하지만,


Node.js 자체에서 JavaScript로 변환하지 않기 때문에 사용할 수 없습니다.


(단, JavaScript에서 공식 지원되면 Node.js에서도 지원될 예정입니다.)


 로컬 모듈을 불러올 때 주의할 점

기존 TypeScript에서는 transpile된 .js 파일을 import하는 것이 일반적이었습니다.

import { myFunction } from './my-module.js';

 

하지만 Node.js는 파일 확장자를 기준으로 모듈 타입을 결정하기 때문에,


이제는 .ts 확장자를 직접 명시해야 합니다.

import { myFunction } from './my-module.ts';

 

개인적으로도 이 방식이 더 직관적이라고 생각하는데요.


tsconfig.json 설정을 통해 프로젝트 전체적으로 적용할 수도 있습니다.


타입을 import할 때는 type을 사용해야 합니다

Node.js는 TypeScript 코드를 변환할 때 타입 관련 문법만 제거하는데요.


이 과정에서 타입을 일반 import로 가져오면 제거되지 않을 수 있습니다.

// 올바른 타입 import
import type { Cat, Dog } from './animal.ts';

// 인라인 타입 import
import { createCatName, type Cat, type Dog } from './animal.ts';

 tsconfig.json 설정

Node.js의 타입 제거(type stripping)는 tsconfig.json을 무시하지만,


개발 중에 타입 검사를 수행하려면 tsconfig.json이 필요합니다.

 

Node.js 공식 문서에서 추천하는 최소 설정은 다음과 같습니다.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "nodenext",
    "allowImportingTsExtensions": true,
    "rewriteRelativeImportExtensions": true,
    "verbatimModuleSyntax": true
  }
}

주요 설정 살펴보기

1️⃣ allowImportingTsExtensions (TypeScript 5.0 이상)
.ts 파일을 직접 import할 수 있도록 허용합니다.

 

2️⃣ rewriteRelativeImportExtensions (TypeScript 5.7 이상)
.ts 파일을 .js로 변환(transpile)할 때, import 문도 .js로 변경합니다.

 

3️⃣ verbatimModuleSyntax (TypeScript 5.0 이상)
→ 타입을 import할 때 type 키워드를 사용하지 않으면 경고를 표시합니다.


--input-type 옵션

파일이 아닌 입력(예: stdin 또는 --eval)을 처리할 때,


Node.js가 코드를 해석하는 방식을 지정할 수 있습니다.

node --input-type=module-typescript

 

지원되는 옵션은 다음과 같습니다.

옵션 설명
module ESM(JavaScript)
commonjs CommonJS(JavaScript)
module-typescript ESM(TypeScript)
commonjs-typescript CommonJS(TypeScript)

타입 제거(Type Stripping)와 Source Map

Node.js의 타입 제거 기능은 "타입 부분을 공백으로 덮어씌우는 방식"을 사용합니다.


이 방식 덕분에 소스 코드의 줄 번호가 그대로 유지되므로,


디버깅할 때 stack trace가 깨지지 않습니다.

예제: TypeScript → JavaScript 변환

입력 (TypeScript)

function describeColor(color: Color): string {
  return `Color named “${color.colorName}”`;
}
type Color = { colorName: string };
describeColor({ colorName: 'green' });

출력 (JavaScript)

function describeColor(color       )         {
  return `Color named “${color.colorName}”`;
}

describeColor({ colorName: 'green' });

 

보시다시피 타입 부분이 공백으로 유지되면서 코드 위치가 그대로 보존됩니다.


앞으로의 변화는?

TypeScript 5.8에서는 미지원 기능을 감지할 수 있음

Jake Bailey (Microsoft TypeScript 팀 개발자)의 말:

"Node.js에서 실행할 TypeScript 코드에서 enum, namespace, decorators 같은
런타임 변환이 필요한 기능을 감지하고 경고하는 플래그가 추가될 예정입니다."

--experimental-transform-types

Node.js에서 TypeScript를 실제로 변환(transpile)하도록 만드는 실험적인 기능입니다.


이 기능이 활성화되면 기존보다 더 많은 TypeScript 기능을 지원하고,


source map도 자동으로 생성됩니다.


몇 가지 개인적인 생각

  • .ts, .tsx, .mts, .cts 같은 다양한 확장자가 너무 많다는 느낌이 있는데요.
    하지만 Node.js와 TypeScript를 함께 쓰려면 필요한 구분이긴 합니다.
  • 타입 제거(Type Stripping) 방식이 향후 브라우저에서도 적용될 가능성이 있을까요?
    즉, JavaScript의 문법 확장은 지원하지 않고, 타입 제거만 지원하는 방향으로요.

마무리하며

이제 Node.js에서 TypeScript를 별다른 설정 없이 바로 실행할 수 있게 되었는데요.


아직 실험적인 기능이긴 하지만, 앞으로의 발전이 기대됩니다.


개발 환경에 맞춰 적절히 활용해보는 것도 좋을 것 같습니다.