노드가 ts 파일을 직접 돌리면 타입은 공백으로 지우고 검사는 하지 않는다. 타입 스트리핑, 이레이저블 신택스, enum, 생성자 공개 인자, tsx, tsc 무출력, 노드 24 LTS, 타입스크립트 5.8, 개발자, API, Next.js 스크립트를 같은 표에 둔다. 넥스트 앱 변환과 파이썬 타입체크 글과 자리를 섞지 않는다.
지난주 배포 스크립트를 tsx 없이 돌리려다 enum에서 멈췄습니다. 노드 22.18부터는 플래그 없이 ts 파일을 실행합니다. 타입은 공백으로 지우고, 검사는 하지 않습니다. 24.12에서 타입 스트리핑이 안정으로 올랐고, 26은 변환 플래그를 뺐습니다. enum과 생성자 공개 인자는 자바스크립트를 새로 만들어야 해서 거부합니다. 타입스크립트 5.8의 이레이저블 신택스 옵션을 켜면 그 문법을 커밋 전에 잡습니다. 저는 크론 스크립트를 노드로 직접 돌리기 전에 이 옵션과 무출력 검사를 CI에 붙였습니다.
넥스트 앱 변환은 프로덕션 체크리스트에 두고, 여기는 노드가 ts를 직접 도는 자리만 봅니다. 숫자는 2026년 8월 노드 타입스크립트 문서와 타입스크립트 5.8 릴리스 노트 기준입니다.
노드가 타입을 공백으로 바꾸는 자리
실행은 합니다. 타입 검사는 안 합니다. 주석만 공백으로 지우고 남은 자바스크립트를 돌립니다.
노드 문서는 이렇게 적습니다. 지울 수 있는 타입스크립트 문법만 기본으로 실행한다. 인라인 타입을 공백으로 바꿔서 줄 번호가 유지됩니다. 소스맵을 만들지 않습니다. tsconfig.json은 읽지 않습니다. 경로 별칭, 옛 문법으로의 다운레벨, 설정에 묶인 변환은 의도적으로 빠집니다. 전체 문법이 필요하면 문서가 예로 든 tsx 같은 서드파티를 씁니다.
버전
바뀐 점
지금 쓸 때
22.6
실험 플래그로 타입 스트리핑 추가
22.18 미만이면 플래그가 필요
22.18, 23.6
기본으로 켜짐. 실험 경고는 24.3, 22.18에서 사라짐
node file.ts만으로 실행
24.12, 25.2
타입 스트리핑 안정
24 LTS를 쓰는 1인 팀의 출발점
26.0
변환 플래그 제거
이넘을 런타임에서 바꿔 주지 않음
끄는 플래그는 --no-strip-types입니다. 끄면 .ts는 그냥 실행되지 않습니다. 정본은 노드 타입스크립트 모듈 문서입니다. 파이썬 쪽 검사 칸은 파이라이트 체크에 있고, 여기는 런타임이 타입을 지우는 자리만 봅니다.
실행과 검사는 한 명령이 아니다 | node file.ts가 통과해도 타입은 틀린 수 있습니다. 노드 배우기 페이지도 검사를 tsc --noEmit으로 분리하라고 적습니다. 로컬에서 바로 도는 것과 CI에서 막는 것을 같은 칸에 두지 않습니다.
enum과 생성자 인자가 멈추는 네 자리
공백으로 지울 수 없으면 에러입니다. 이넘, 값이 있는 네임스페이스, 생성자 공개 인자, import 별칭이 그 네 칸입니다.
노드는 타입스크립트 문법을 자바스크립트로 다시 쓰지 않습니다. 지울 수 있는 주석만 뺍니다. 그래서 enum Direction { Up, Down }은 ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX로 멈춥니다. 값이 있는 namespace도 같습니다. 타입만 내보내는 네임스페이스는 됩니다. 클래스 생성자에 public x: number를 붙이는 자리, import foo = require("foo")와 export =도 런타임 코드가 생겨서 거부됩니다.
문법
노드 직접 실행
바꿀 때
타입 주석, 인터페이스, 타입만 있는 import
됨. 공백으로 지움
그대로 둠
enum
거부
as const 객체와 유니온
값이 있는 네임스페이스
거부
모듈로 나눔. 타입만 있는 칸은 유지
생성자 공개 인자
거부
본문에서 this.x = x
import =, export =
거부
표준 import, export
데코레이터, .tsx
미지원
tsx나 넥스트 변환
타입스크립트 5.8의 erasableSyntaxOnly가 같은 네 칸을 컴파일 단계에서 막습니다. 노드가 나중에 던질 에러를 에디터에서 먼저 봅니다. 블룸버그 ts-blank-space와 노드 아마로가 같은 한계를 가집니다. 릴리스 노트는 타입스크립트 5.8 문서입니다.
이넘을 상수 객체로 바꿔 노드가 받게 하는 예
// 거부. 런타임 객체를 만들어야 함
enum Direction {
Up,
Down,
}
// 허용. 지울 타입이 주석뿐
const Direction = {
Up: 'Up',
Down: 'Down',
} as const
type Direction = (typeof Direction)[keyof typeof Direction]
class Point {
x: number
y: number
constructor(x: number, y: number) {
this.x = x
this.y = y
}
}
이넘과 생성자 공개 인자는 공백으로 지울 수 없다. 상수 객체와 본문 대입으로 바꾼다
tsconfig는 노드가 읽지 않는다
설정 파일은 에디터와 tsc용입니다. 런타임은 확장자와 package.json의 type만 봅니다.
문서가 권하는 설정은 타입스크립트 5.8 이상, target은 esnext, module은 nodenext입니다. erasableSyntaxOnly로 지울 수 없는 문법을 막고, verbatimModuleSyntax로 타입 import에 type 키워드를 강제합니다. rewriteRelativeImportExtensions는 상대 경로의 .ts를 빌드 결과에서 .js로 바꿉니다. 스크립트만 직접 실행하면 noEmit을 켭니다. 배포물에 js를 남기면 이 옵션은 끕니다.
모듈 칸은 파일 확장자가 정합니다. .mts는 ESM, .cts는 CommonJS입니다. .ts는 가까운 package.json의 "type": "module"을 따릅니다. import './file'처럼 확장자를 빼면 거절됩니다. import './file.ts'가 맞습니다. tsc가 그 경로를 받으려면 allowImportingTsExtensions가 필요합니다. paths 별칭은 변환되지 않습니다. 가까운 대체는 #로 시작하는 서브패스 imports입니다.
type 없이 타입만 가져오면 런타임 에러입니다. 노드는 그 줄을 값 import로 남깁니다. import type { Row }이거나 import { fn, type Row }처럼 칸을 나눕니다. 정본 설정은 같은 타입 스트리핑 절에 있습니다.
로컬은 node script.ts로 빠르게 보고, 머지 전에 tsc --noEmit을 한 번 돌립니다. 두 명령을 한 줄로 합치지 않습니다.
12사이트 크론은 예전에 tsx로 한 줄을 감쌌습니다. 의존이 늘고, 깃허브 액션 이미지가 커졌습니다. 노드 24로 올린 뒤 배포 스크립트는 node ./scripts/foo.ts로 바꿨습니다. 그날 바로 깨진 파일은 상태 이넘이 있는 한 개였습니다. 상수 객체로 고치고, 저장소 루트 tsconfig에 이레이저블 옵션을 켰습니다. 그다음부터는 푸시 전에 에디터가 같은 자리를 밑줄로 그었습니다.
테스트 러너는 이 글의 주제가 아닙니다. 비테스트와 제스트 비교는 비테스트 비교에 있습니다. 파이썬 패키지 칸은 uv 글에 있습니다. 여기는 런타임이 타입을 안 본다는 한 줄만 남깁니다. 한국 1인 팀은 액션 분을 아끼려고 검사 단계를 빼기 쉽습니다. 빼면 enum을 고친 줄이 메인에 들어가고, 크론이 새벽에 죽습니다.
실행은 노드, 검사는 tsc, 변환이 남은 자리만 tsx다. 한 도구에 세 칸을 맡기지 않는다
넥스트 앱과 노드 스크립트는 같은 설정이 아니다
넥스트는 자체 변환으로 .tsx를 돌립니다. 노드 내장 스트리핑은 그 파일을 받지 않습니다.
앱 라우트의 페이지와 서버 액션은 넥스트가 타입스크립트를 처리합니다. 그 칸에 노드 직접 실행 설정을 복사하면 확장자 규칙과 경로 별칭이 엇갈립니다. 반대로 scripts/ 아래 발행, 사이트맵, 썸네일 생성은 넥스트 밖입니다. 그 칸이 이 글의 대상입니다. 저는 루트 tsconfig를 앱용으로 두고, 스크립트 폴더만 이레이저블 옵션을 켠 작은 설정을 물립니다. 한 파일에 두 환경을 섞으면 paths가 스크립트에서 터집니다.
의존성 안의 ts는 노드가 거부합니다. node_modules 아래 타입스크립트를 직접 실행하지 말라는 뜻입니다. 패키지를 배포할 때는 js를 남깁니다. 배우기 페이지의 패키지 발행 절과 같습니다. 자바 가상 스레드처럼 런타임 기본값이 바뀌는 자리는 히카리 글과 성격이 비슷합니다. 기본이 켜졌다고 예전 문법을 그대로 두면, 새 런타임에서 먼저 죽습니다.
타입 import는 type 키워드를 붙인다
import type { PostMeta } from './types.ts'
import { loadPost, type LoadOpts } from './load.ts'
export async function run(opts: LoadOpts) {
const row = await loadPost(opts)
return row.id
}
tsx를 남길 자리와 빼는 자리
리액트 tsx, 데코레이터, 이넘을 당장 못 지우는 레거시만 tsx를 둡니다. 크론과 순수 스크립트는 노드 직접 실행으로 옮깁니다.
노드 문서도 전체 문법 지원의 예로 tsx를 적습니다. npx tsx file.ts 또는 node --import=tsx file.ts입니다. 26에서 변환 플래그가 빠졌기 때문에, 이넘을 런타임이 대신 바꿔 주길 바라면 서드파티가 맞습니다. 다만 의존을 하나 더 두는 대가입니다. 저는 발행 스크립트 여섯 개를 상수 객체로 고치는 데 반나절이 걸렸습니다. 그다음 액션에서 tsx를 뺐고, 설치 시간이 눈에 띄게 줄었습니다.
자리
실행
이유
크론, 발행, 사이트맵 스크립트
node file.ts
지울 문법만 있으면 의존이 필요 없음
넥스트 페이지, 서버 액션
넥스트 빌드
.tsx와 앱 변환
리액트 컴포넌트를 노드에서 렌더
tsx
내장은 .tsx 미지원
이넘을 당장 못 지우는 레거시
tsx, 기한 후 이전
26은 변환 플래그가 없음
한국 호스팅 이미지에 노드 22.17이 남아 있으면 플래그가 필요합니다. 가비아, 카페24 애플리케이션 노드, 깃허브 액션 actions/setup-node의 기본값을 먼저 봅니다. 22.18 이상이면 플래그 없이 됩니다. 배우기 페이지는 네이티브 실행 가이드입니다. tsx 설치 예는 노드가 가리키는 tsx 문서입니다.
스크립트는 노드, 앱은 넥스트, 컴포넌트 렌더만 tsx다. 세 칸을 한 명령으로 묶지 않는다