
우리 회사는 Node.js를 이용해 구축한 서버를 사용해 API를 서비스하고 있다. 그런데 얼마 전 ES6를 초과하는 문법을 사용할 때 에러가 발생했다. 마침 업무적으로 여유가 있을 때라 알아보고 해결하기로 마음먹었다.
package.json을 열어보고 Babel 관련 모듈 버전을 살펴보았다. 역시나 @babel/preset-env 버전이 캐럿(^)을 사용하지 않고 과거 버전으로 고정되어 있었다. 나는 당장 @babel/preset-env, @babel/core를 최신 버전으로 업그레이드했다.
하지만 여전히 최신 ES 문법을 지원하지 않고 에러를 뿜어댔다. 🤦♂️ 문득, 서버를 번들링 하는 부분이 의심됐다. backpack-core를 이용해 번들링 하는데 그 과정에 .babelrc 파일을 읽어 구형 문법으로 Transpile하고 있었다. 원인은 알 수 없었지만 .babelrc 파일을 정상적으로 읽지 못하는 것 같아 관련 레퍼런스를 찾기 위해 여기저기 삽질하며 돌아다니는데...
문득, 서버에서 번들링이 필요할까? 라는 생각이 들었다. 런타임이 브라우저라면 사용자의 사용성 향상을 위해 Webpack과 같은 번들링 도구를 활용하지만, 런타임이 Node인데 굳이 번들링을 할 필요가 있을까? 바로 멘토에게 물어봤다. 멘토님도 나와 같은 의견이었다.
기존에 사용하던 backpack을 걷어내기로 했다. 개발환경에서 backpack은 backpack dev 커맨드를 실행하면 webpack dev server와 같이 hot-loader 기능을 제공하고 있었다. 나는 이 부분을 babel-watch 모듈로 변경했다.
{
"presets": ["@babel/env"],
"plugins": [
[
"module-resolver",
{
"root": ["./server"],
"alias": {
"@framework": "./server/framework",
// ...
}
}
],
"@babel/plugin-transform-runtime"
]
}
수정하는 김에 Path alias를 위해 module-resolver 플러그인도 같이 적용했다. eslint에서 path 관련해 에러를 내뿜기 때문에 .eslintrc에 import/resolver도 적용해야 하고, jsconfig.json 문서도 수정해야 하지만 이번 포스팅에서는 다루지 않겠다.
"scripts": {
"build": "node build.js && babel --extensions .ts src -d dist",
"start": "npm run build && node ./dist/index.js",
"dev": "babel-watch --extensions .ts src/index.ts"
},
"dependencies": {
"@babel/runtime": "^7.17.2",
// ...
}
"devDependencies": {
"@babel/cli": "^7.17.3",
"@babel/core": "^7.17.2",
// "@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"babel-plugin-module-resolver": "^4.1.0",
"babel-watch": "^7.7.0",
// ...
}
간략하게 devDependency를 설명하면,
@babel/node 사용 시 주의사항
You should not be using babel-node in production. It is unnecessarily heavy, with high memory usage due to the cache being stored in memory. You will also always experience a startup performance penalty as the entire app needs to be compiled on the fly.
요약하면, babel-node는 운영환경에서 사용하면 안 된다. babel-node는 무겁고 캐시로 인해 높은 메모리 용량을 사용하므로 개발환경에서만 사용해야 한다.
nodemon + babel/node 조합은 re-load 되는 시간이 너무 길었다. ㅠㅠ (약 30초가량 소요) 개발자가 변경사항을 저장할 때마다 babel/node는 전체 파일을 transpile한다. 이 과정이 너무 길어 불편했다.
이를 해결하기 위해 babel-watch 모듈을 사용했다.
babel-watch description:
babel-watch only starts babel in the "master" process where it also starts the file watcher. The transpilation is performed in that process too. On file-watcher events, it spawns a pure node process and passes transpiled code from the parent process together with the source maps. This allows us to avoid loading babel and all its deps every time we restart the JS script/app.
변경사항 발생 시 전체 코드를 transpile 하는 게 아닌, 관련 소스 코드만 transpile을 수행하도록 했다.

Node 버전에 따라 ES 문법 지원 범위가 달라진다. 위 이미지를 보면, ES2020 문법인 Optional chaining operator(?.)는 Node 12 버전에서는 동작하지 않는다. 최신 문법 사용하는데 Node 버전 종속성을 회피하고 싶어 Babel을 사용했다.