목차
app.ts를 JS 파일로 컴파일하는데 아래와 같은 에러가 났다.
// app.tsimport express from 'express'; const app = express(); app.listen(5000, () => { console.log('Server is connected to 5000'); });
TS1259: Module "/Users/minha/Documents/projects/mirae-real-estate/server/node_modules/@types/express/index can only be default-imported using the 'esModuleInterop' flag
그리고 그걸 개의치 않고 노드를 실행해보니 다음과 같은 오류가 났다.
ReferenceError: exports is not defined in ES module scope This file is being treated as an ES module because it has a '.js' file extension and '/Users/minha/Documents/projects/mirae-real-estate/server/package.json' contains "type":"module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
보아하니 CommonJS와 ES Module의 호환성 문제인 것 같은데... 하나씩 파헤쳐 보기로 한다.
컴파일에서 난 에러 (esModuleInterop)
esModuleInterop 옵션이 제대로 설정 되어있지 않다는 에러.
esModuleInterop
- 일반적으로, ES 모듈은 import 문법을 사용하여 모듈을 가져오고, CommonJS 모듈은 require() 함수를 사용하여 모듈을 가져온다.
- esModuleInterop 옵션을 활성화하면 TypeScript는 import 문법을 사용하여 CommonJS 모듈을 가져올 수 있다. 이를 통해 ES 모듈과 CommonJS 모듈 간의 호환성을 개선할 수 있다.
그런데 나는 tsconfig.json에서 해당 옵션을 true로 값을 설정하고 있었다.
// tsconfig.json { "compilerOptions": { "types": ["node", "express"], "target": "ES6", "module": "ES6", "strict": true, "esModuleInterop": true }, "include": [ "./src/**/*.ts",// 컴파일할 TypeScript 파일 경로 및 패턴을 추가하세요"./app.ts", "winston.ts" ], "exclude": [ "node_modules"// 제외할 디렉토리 또는 파일을 추가하세요 ] }
무엇이 문제인지 구글링을 하던 중 express 라이브러리는 사실 타입스크립트를 지원하지 않는다는 사실이었다.
따라서 타입스크립트를 적용 시켜주려면 @types/express라는 라이브러리를 따로 다운로드하여야 했다.
@types
- 일반 자바스크립트 라이브러리는 타입 정보를 포함하고 있지 않기 때문에, 타입스크립트에서 자바스크립트 라이브러리를 사용할 때에는 해당 라이브러리의 타입 정보를 별도로 제공해야 한다.
- @types 패키지는 npm의 DefinitleyTyped라는 프로젝트에서 제공하는 패키지이다. DefinitleyTyped 프로젝트는 타입스크립트 커뮤니티에 의해 관리되며, 오픈소스로 이루어져 있다.
라이브러리가 타입스크립트를 지원하는지 안 하는지는 npm 사이트에서 확인할 수 있다.
알고 보니 많은 라이브러리들이 아직 타입스크립트를 지원하지 않는다는 사실을 발견했다... 심지어 노드 마저...!
노드의 경우에는 앞으로도 타입스크립트를 지원할지 의문이다. 왜냐하면 노드의 창시자 라이언 달(Ryan Dahl)이 이미 타입스크립트 런타임 환경인 Deno를 개발했기 때문이다.
따라서 아래와 같이 @types 패키지를 이용해 express와 node를 다시 설치해 주었다.
그럼에도 불구하고 에러는 똑같이 발생했다...
CommonJS와 ES6 호환성
그래서 이번엔 CommonJS와 ES6 호환성에 초점을 두고 문제를 해결해보려 한다.
현재 타입스크립트와 노드는 ES6 문법을 쓰게끔 옵션들을 맞춰둔 상태. 사실 전부 CommonJS 문법을 사용하게 옵션들을 바꿔주었으면 되었지만 나는 requrie 문법 말고 import/export 문법을 쓰고 싶었다.
그래서 마음에 들지 않았지만 우선 package.json에서 type: module 옵션을 제거해 보기로 했다.
"type" : "module"
노드는 기본적으로 CommonJS 문법을 제공한다. 그래서 ES6 문법을 쓰고 싶다면 (import/export) 해당 코드를 package.json에 추가해 주어야 한다.
아니나 다를까 아무런 문제 없이 너무나도 간단하게 해결...!
노드 환경과 타입스크립트 설정에서 다 ES6 문법을 적용할 수 있도록 설정했는데 무엇이 문제인 건가 고민해 보다 문득 든 생각이
'아 노드와 기존 express 라이브러리에서는 노드의 package.json에 저렇게 코드 한 줄 추가하면 ES6 문법을 사용할 수 있게 만들어 놓았지만, 커뮤니티에서 정의된 @types/express 패키지에서는 그게 호환이 되지 않는 게 아닐까..?'라는 추측이 들었다.
결론적으로 CommonJS와 ES6 문법의 호환성 문제 였다.
P.S.
모듈을 불러올 때 이렇게 namespace import 형식으로 불러와야 에러가 나지 않는다.
추측하는 바로는 namespace import는 해당 모듈의 모든 객체를 가져오고, default import의 경우 모듈의 단일 객체를 가져오는데....
이게 어떻게 다르게 적용되는지는 아직 공부가 더 필요하다.
import * as express from 'express';
Share article