Node.js, 추가 설정 없이 TypeScript 파일 실행 지원
(nodejs.org)- Node.js가 TypeScript 파일을 직접 실행할 수 있게 개선됨
- 이제 추가적인 설정이나 트랜스파일링 없이도
.ts
파일 바로 실행 가능해짐 - 개발자는 tsconfig.json이나 별도의 번들러 설치 없이 작업 효율성 증진 가능
- 해당 기능은 Node.js v22.18.0 (LTS) 버전부터 공식 반영됨
- JavaScript와 TypeScript 개발 간의 경계가 완화되는 결과 기대
Node.js의 TypeScript 직접 실행 지원
- Node.js는 최근 v22.18.0 (LTS) 버전에서 TypeScript 파일(.ts) 을 별도의 설정이나 도구 없이 직접 실행할 수 있는 기능을 도입함
- 기존에는 TypeScript 코드 실행을 위해 ts-node, esbuild, Babel 등 외부 트랜스파일러 또는 번들러가 필요했으나, 이제 이런 도구 없이도 Node.js 자체에서 TypeScript 코드를 인식하고 실행함
- 이 기능을 통해 개발자는 tsconfig.json 구성파일이나 추가 라이브러리 없이
.ts
파일을 Node.js에서 곧바로 실행 가능함 - 프로토타이핑, 실험적 개발, 스크립트 실행 등에서 생산성과 개발 편의성이 크게 증가
- JavaScript와 TypeScript 프로젝트 간의 연계성 강화, 신규 개발자 진입 장벽 완화 등의 효과가 기대됨
그외 주목할 만한 변경
-
esm:
import.meta.main
구현 - fs: AsyncIterator 기반의 fs 이벤트 처리 개선
- permission: 하위 프로세스 실행 시 권한 모델 플래그 전달 지원
-
sqlite:
readBigInts
옵션 추가 -
src/permission:
permission.has(addon)
지원 -
url:
fileURLToPathBuffer
API 추가 -
watch:
--watch-kill-signal
플래그 추가 -
worker:
Worker
객체를 async disposable로 개선
커밋 및 문서 관련 업데이트
- 불필요 코드 제거, 빌드 환경 및 도구 체인 정비, npm 10.9.3 업그레이드 포함
-
globals.md
,child_process.md
,http2
등 문서의 세부 안정성 지표 및 RFC 번호 수정 - 다수의 테스트 추가 및 버그 수정 반영
배포 파일
- Windows, macOS(Intel/Apple Silicon), Linux(x64, ARM, PPC, s390x, AIX)용 설치 파일 및 바이너리 제공
- 소스 코드와 전체 릴리스 파일은 Node.js 공식 배포 페이지에서 다운로드 가능
- API 문서는 v22.18.0 기준으로 업데이트됨
간단한 스크립트 실행에는 괜찮을 것 같은데 라이브 프로젝트에는 제약이 많아서 사용할 일이 별로 없어보이네요.
ERR_MODULE_NOT_FOUND/ERR_UNSUPPORTED_DIR_IMPORT 오류로 확장자나 경로도 맞춰줘야 하고
NestJS 같이 "emitDecoratorMetadata" 설정으로 TypeScript 빌드 지원이 필요한 기능은 못 쓰고 그러니...
따봉을 참을 수가 없네여.
--no-experimental-strip-types
플래그로도 충분히 좋다고 생각했지만.
더욱이 좋은 것 같습니다.
Hacker News 의견
- Node.js에서 node:test와 함께라면 이제 Node.js가 거의 대부분의 케이스에서 설득력 있는 디폴트 선택지라고 생각함. tsx를 사용해서 실행하는 게 큰 삶의 질 향상이었으나, 여전히 완전하지 않았었음. zod와 ts-rest, trpc 같은 툴로 엣지에서의 런타임 타입 단언이 주로 해결되고 있고, 요즘에는 풀스택 Typescript 개발이 정말 쉬워짐
- 진짜임. 2025년이 되어 드디어 node 생태계가 기본 설정만으로도 쓸 만해짐. ESM 모듈이 Node와 Typescript 양쪽에서 그냥 잘 동작하고, Node가 .ts 파일을 바로 실행할 수 있으며, 좋은 수준의 기본 테스트 러너를 내장하고 있음 (--watch 지원). 내장 패키지들도 더 좋아지고 있음. node:fs/promises 같은 경우, top-level await 덕분에 비동기 루프 작업도 훨씬 쉬워짐. 모두가 현실적으로 접근하도록 설득하려면 시간이 오래 걸렸지만, 이제 정말 쾌적해진 상황임
- Node에서 Typescript를 직접 지원하는 거에 적극 찬성함. vitest가 요즘엔 많은 부분을 편하게 해주고 있지만, .ts 파일 테스트 환경 설정보다 정말 많은 시간을 소모했었음. trpc와 ts-rest는 완전히 다른 문제라고 생각. 둘 다 쓸 수 있지만, 프로덕션에서는 trpc의 경우 API URL을 내가 직접 소유할 수 없고, 오래된 URL을 자연스럽게 관리하며 폐기하지 못하기 때문에 피하고 있음. ts-rest의 경우에도 보통 직접 zod와 타입을 공유해서 API 요청/응답 쌍을 직접 관리하는 걸 선호함. 그리고 trpc가 명백한 RPC 툴인데 이름에 -rest가 붙어있을 때마다 거슬림
- 앞으로 Sveltr가 코드베이스를 다시 Typescript로 옮기는지 한번 지켜볼 예정임
- 이게 tsx를 실행하는 건지 궁금함. 타입을 제거해도 JSX는 JavaScript로 변환해야 하는데, 그 부분이 궁금함
- 난 얼마 전에 Python으로 전환함. 모든 게 내장되어 있어서, 완성되지 않은 시스템의 여러 기괴한 문제를 디버깅하지 않아도 되어 훨씬 만족스러움
- Node가 최근 몇 년간 보여준 발전에 큰 인상을 받았음. deno와 bun이 Node를 자극해서 집중적이고 의미 있는 개선 작업을 하게 만든 것이 좋았음. 한동안 정체되어 있었음
- Node에 최근 어떤 개선이 있었는지 궁금함. 마지막으로 의미 있다고 느낀 개선이 임포트/익스포트의 정식 지원이었음 (.mjs 해킹이 여전히 필요한지 모르겠지만). 얼마간 생태계에서 떨어져 있어서, 그 이후에 무슨 변화가 있었는지 알고 싶음
- 과연 그랬는지 의문임. 내가 참여한 프로젝트들을 보면 deno나 bun이 없어도 전혀 무방했음. 실질적으로 의미 있는 건 node LTS 릴리스뿐이고, 최신 프로젝트들도 여전히 node 20 버전을 사용하고 있음
- 타입스크립트가 node_modules에서 받아들여지지 않는다는 점이 아쉬움 (관련: node.js 공식 문서).<br>그렇다면 프로젝트 의존성은 어떻게 되는지 궁금함. 데이터 모델용 라이브러리를 Typescript로 작성했는데, Typescript 상태로 내 앱에 임포트하고 싶음. 이 규칙이 npm 패키지에만 적용되는지, 아니면 모든 의존성에 적용되는지 궁금함. 여기에 기회가 있음.<br>나는 typescript (정확히는 JS 전반) 실행을 위한 golang 기반 런타임을 만들었고, grafana 팀이 사용하는 sobek도 타입 스트립 기능만 추가하면 될 듯함. 완전히 Typescript를 기본 채택한 런타임이 하나만 나오면 Node.js가 정말 혁신될 거라 느끼고 있음. 트랜스파일러도, typescript-go도, rust도 필요 없고 (그래도 rust는 약간😉), 좋은 파서가 소스맵과 타입을 디버깅 모드에서 추적해주는 시스템이면 충분함.<br>어쨌든 Node 팀과 모든 기여자들에게 인정과 감사를 표함. 표준이 되는 Node 덕에 우리 모두가 그 뒤를 따르는 느낌임. 임베딩 API도 간단하고 사용성이 깔끔해서, 독립 실행형 만들 때도 편리함
- 동일한 코멘트를 남긴 적 있음 (참고). “타입스크립트로 작성된 패키지를 npm에 배포하지 않도록 장려하기 위한 것” 부분이 있는데, 나도 프라이빗 패키지로 시도해봤지만 동작하지 않았고, Node는 “private” 필드 자체를 신경도 쓰지 않음
- 패키지는 npm에 배포하기 전에 반드시 JavaScript로 컴파일해서 배포해야 한다고 생각함. TypeScript를 그대로 npm에 올려야 할 이유가 없다고 봄
-
관련 이슈<br>node_modules에서 타입 스트립 지원이 없는 게 아쉽다고 생각함
- 이 기능을 기대했던 이유의 절반은 바로 이것임. TypeScript로 라이브러리를 작성하고, 타입체크는 CI/CD에서만 진행한 뒤 Node.js에서 바로 임포트할 수 있길 바랐음
- 올바른 결정이라고 봄. TypeScript는 브레이킹 체인지가 꽤 자주 발생함. 표준화되지 않는 한, 현재 방식이 낫다고 생각함
- 나는 JS/TS를 많이 쓰는 쪽은 아니지만, Bun을 그냥 쓰는 게 더 나은 거 아닌가 궁금함. 모든 프로젝트가 새롭게 시작되는 건 아니겠지만, Bun이 전반적으로 더 좋은 런타임이라고 느꼈음. TS 실행이 처음부터 되고, 의존성 해석도 훨씬 빠르며, 사용성도 뛰어남. 개인적으로 옛 Node 프로젝트를 Bun으로 많이 옮겼고, Bun 공개 이후 Node는 거의 쓰지 않게 됨. 혹시 내가 잘못 알고 있는 점이 있으면 알려달라 바람
- Node만 8년 가까이 썼다가 최근에 Deno로 갈아탔었음. 이 전환도 쉽진 않았고, 실제로 동작 안하는 게 아니라 언제 안 돌아갈지 모르는 상황이 두려웠기 때문임. Node도 분명 아쉬운 부분이 많지만, 그래도 업계의 사실상 표준 기준이 됨. JS 생태계 자체가 혼란스러워서, 많은 개발자가 새로운 빌드툴이나 번들러, 런타임에 지친 상태임. Bun이 충분히 설득력 있어질 정도로 안정성이나 지원이 쌓이지 않은 거 같음. 이전에 TS의 마이너 업데이트 하나로 며칠씩 문제를 해결했던 경험도 있음
- 난 최신 트렌드를 쫓는 게 썩 달갑지 않음. NodeJS는 JS 생태계에서 가장 지원이 잘 되는 런타임임. 기본값이 되는 선택지를 따라가는 게 훨씬 낫다고 생각함. 소위 ‘심심한’ 기술을 선택하는 편임
- Bun 출시 이후 완전히 전환을 여러 차례 시도했지만, 매번 90%쯤에서 피할 수 없는 문제에 부딪힘. 마지막 시도엔 일부 라이브러리에서 napi 함수가 미구현이라 작동이 안 됐고, opendir 옵션에서 recursive가 무시되는 문제 등 기억나는 이슈들이 있었음. Bun이 따라잡기를 기다리는 입장이지만, 아직까지 대형 프로젝트에 실무 적용할 준비가 된 것 같진 않음. bun 전용 기능도 첫인상은 좋아 보이나 실전에서는 미흡하다고 느낌. 문서도 Node.js만큼 품질이 높지 않음
- Node를 Bun으로 대체 시도했을 때 겪은 호환성 문제들임.<br>- TCP 연결에서 localAddress 무시됨<br>- Node 모듈 API와의 비호환성 (예시: spamscanner 프로젝트 미작동)<br>- EventEmitter 관련 race 문제 (부분 해결: eventemitter2로)<br>- Svelte vites dev 서버가 종종 멈춰서 node_modules 삭제 후 재설치해야 했음
- Node 자체의 TS와 테스트 러너 기능을 사용해봤는데, 아직 Bun만큼 좋지 않음. 당분간 그런 기능을 쓸 때는 Bun을 쓰고 있음. Node 생태계에서는 한 가지에만 올인하기보단, 각기 특화된 도구를 적절히 조합하는 게 더 낫다는 걸 배움.<br>
- Bun.js: Node 런타임용, TS 실행과 테스트에 활용. TSX, TS-Node, Node 자체 등 다양하게 시도해봤음<br>
- NPM: 툴링 스크립트 실행에 사용<br>
- PNPM: 의존성 설치에 사용. (npm, yarn, bun 대비 가장 우수하다고 느낌)<br>
- Biome.js: 린팅용. 지금까지 쓴 그 어떤 린팅 툴보다 뛰어남
- 이번 개선이 “타입 스트리핑”만으로 이루어진 점이 정말 좋은데, 소스맵이 필요 없어서 프로덕션에선 완전한 제로코스트가 됨. Node 팀 정말 잘했다고 생각함
- 타입 스트립 함수 (
import { stripTypeScriptTypes } from 'node:module'
)도 노출되어 있음. 프론트엔드 의존성이 없는 간단한 웹앱 개발 시, 전체를 타입스크립트로 만들고 프론트엔드 스크립트도 서빙할 때 타입만 빼버리면 됨 (예시 프로젝트) - 이 변화 덕분에 우리 회사도 드디어 Typescript로 옮겨갈 수 있었음. 여러 서비스를 한꺼번에 TS로 전환했고, 일부는 진행 중임. 큰 수확임
- 동작 방식이 타입 정보를 제거하는 것 같음. 즉, 트랜스파일 단계 하나를 줄여주는 것이고, 안전성 면에서는 향상되는 것은 아님
- 타입스크립트 주요 설계 목표는, 타입 관련 구문만 제거하면 결과물이 유효한 JavaScript 파일인 것임. TS 컴파일러는 코드를 생성하지 않음 (예: PureScript와 다름). tsc 등 타입체커를 통해 정적 검사를 하게 되고, 타입 정보는 제거됨. 파이썬도 런타임에 타입 애너테이션을 무시하고, Java 역시 바이트코드에서 일부 제너릭 등 타입 정보가 삭제되는 특성을 가지고 있음
- “최소한 트랜스파일 단계를 줄여준다는 것뿐 안전성은 개선되지 않는다”는 말은 조금 오해의 소지가 있음. Node가 TS를 바로 실행하는 게 타입체킹 안전성을 높여주는 건 아니지만, 타입체킹은 에디터나, 다른 여러 툴에서 별도로 진행 가능함. Node가 TS 바로 실행을 지원함으로써 TS 사용의 진입장벽을 확 낮춰주고, 간접적으로 타입 안정성을 도와줌
- 타입스크립트는 안전성 향상까지 보장하겠다는 약속을 한 적 없음. 보통의 오해이긴 함. TS는 런타임 모드나 정보가 없는 셈이었고, 타입체커를 돌리지 않으면 심각한 타입 오류가 있어도 그대로 실행 가능했음. 엄밀히 말해 Typescript는 린터에 더 가까움
- 빌드/트랜스파일 없이 바로 스크립트 실행할 수 있다는 건 엄청 편리하다고 생각함. 타입체크 필요하면 tsc로 돌리는 방식이 나한텐 잘 맞음
- tsx를 써서 똑같이 빌드/트랜스파일 필요 없이 실행하는 셋업을 프로젝트에서 쓰고 있음. 개발 중엔 굉장히 유용함. tsx의 --watch 덕분에 TS 소스에서 바로 서버 돌리고, 변경되면 자동 리스타트함. 앞으로 nodemon과 Node 내장 기능만으로 비슷한 환경 만들 수 있을 듯. 런타임에서 타입체킹까지 하려면 v8 레벨에서 지원해야 되는데, 이건 거의 전체 재작성급 작업일 것임
- 실제로는 Typescript 전체를 실행하는 게 아니라, 일부 서브셋만을 지원함. 타이틀 내용이 과장됐다고 느껴질 수 있음. 이런 변화가 TS를 린터처럼만 쓰게 만들 우려가 있고, 타입스트립 만으로 구현 불가한 다양한 강력한 TS 기능이 사장될 수 있음
- TS에서 스트립 처리 불가한 기능은 대체 무엇이 있는지 궁금함. enum 외에 실제로 쓰는 사례가 어떤 게 있는지 질문함