ts 파일을 간단하게 실행 시킬 수 있는 인터프리터 환경인 ts-node를 사용하는 과정에서 겪은 에러다.
원래 ts는 js 로 컴파일 과정을 거치고 실행을 해야되는데
ts-node 를 쓰면 노드 환경에서 그냥 바로 ts 를 실행시킬 수 있다.
npx ts-node example.ts
난 그대로 따라했는데 에러가 났다.
TypeError: Unknown file extension ".ts" for 어쩌구
신기하게도 동일한 에러가 난 사람이 디코방에 질문을 올린게 있었다.
이 답변에 달린내용을 제대로 이해해보고자 기록을 남겨두려한다.
1. commonJS / ESM
js는 원래 브라우저 내에서 사용하던 언어인데, 그걸 브라우저가 아닌 다른 공간에서도 사용하고자 생긴 모듈 시스템 중 하나가 commonJS 이다. 초기 node.js에서는 commonJS 모듈을 채택했다.
ESM은 ES(ECMAScript, javascript 표준 사양) Module 이다.
두개의 가장 큰 차이점은 비동기 유무인데, ESM이 비동기 방식이다.
그래서 브라우저에서 직접 ESM 을 지원한다. 네트워크 요청을 할때 빠르게 모듈만 비동기로 사용할 수 있도록 해준다.
따라서 시간이 지나면서 자연스레 node.js 도 특정 버전 이후부터는 ESM도 지원하게 되었다.
그러나 기본 컴파일 모듈은 commonJS 방식을 따른다.
2. node 와 ts-node의 상관관계
node는 기본적으로 commonJS 모듈로 컴파일된다.
ts-node는 원래 CommonJS 모듈 시스템을 사용하는 Node.js 환경에 맞춰져 있었으나, node가 ESM을 지원하게 되면서, ts-node 또한 특정 버전 이후부터는 ESM 방식으로 컴파일을 하게 되었다. <- 여기서 문제가 생기는 것이다.
3. ts-node 의 설정
내가 이때는 tsconfig.json 파일을 생성해주지 않았었다.
왜냐면 애초에 ts-node 자체가 tsc 없이 바로 노드에서 실행시킬 수 있는 인터프리터 환경이기 때문에, 그 편의성을 누려야 한다고 생각해서(?)였다.
ts-node는 따로 컴파일 옵션을 지정해줄 수 있는데 tsconfig.json 파일이 있으면 여기있는 설정을 따른다.
결국 나는 tsc 를 설치하고 기본 설정 파일을 생성해주었다..
그리고 바로 실행해보았다.
아주 잘 실행되었다..
여기서 해당 설정 부분은
{
"compilerOptions": {
"module": "commonjs",
}
}
이 부분이다.
이부분은 설정을 지워도 에러 없이 동작하는데, tsconfig.json의 기본 설정이 "module" : "commonJS" 여서 이 부분을 "ESNex" (ESM 방식으로 컴파일) 로 설정해 주지 않는 이상 commonJS 로 컴파일하도록 인식하기 때문인것 같다.
4. ESM 방식으로 실행
그렇다면 node.js 의 기본 설정 자체를 ESM 으로 바꾸면 어떻게 될까?
ts-node의 실행 방식을 ESM으로 바꾸고 ts-node 의 컴파일 방식도 ESM 을 적용할시에도 문제없이 돌아갈까?
또 다른 추가 설정을 해줘야 했지만 결론은 O 였다.
node.js의 ESM 설정 >
// package.json
{
...
"type" : "module",
...
}
ts-node 의 ESM 설정 >
여기서 헷갈렸던게 "ts-node는 따로 설정해주지 않아도 ESM 으로 돌아가지않나?" 라고 생각했으나 (애초에 그래서 node와 호환성 문제가 생겼던거니까),
막상 그냥 실행해보니 이번에는 또 다른 에러 (Must use import to load ES Module : commonJS 방식으로 작성된 파일을 ES 방식으로 읽으려다보니 규칙이 충돌하는 부분이 있음)가 생겨서.. 결국 tsconfig.json파일을 수정하여 또 다른 설정을 해줘야 했다.
// tsconfig.json
{
module : "NodeNext", // moduleResolution에 따라 맞춰줌
moduleResolution : "nodenext" // cjs 파일과 mjs 파일의 충돌을 없애주는 설정
}
// 실행시
node --loader ts-node/esm [파일명]
실행시에도 로더를 지정해줘야 했다.
commonJS 방식에서는 노드가 알아서 이해하기 때문에 ts-node가 알아서 ts -> js 로 변환해서 읽었지만 ,
ESM 방식에서는 node가 ts 파일을 읽으려면 로더를 지정해줘야 하기 때문이다.
위 세개의 설정을 각각 해주면 ESM 방식으로 ts 파일을 실행할 수 있었다.
commonJS와 esm 의 차이를 깊이있게 알지 못했는데 에러가 왜 났는지 살펴보면서 좀 더 근본적인 원인을 알게 된 것 같아서 좋은 경험이었따.
끗