Node.js Module system (feat.AWS Lambda)

Falcon·2021년 2월 23일
3

javascript

목록 보기
18/28
post-thumbnail

Tell me what you wanna say!

require,module.exports
import, export 키워드들
하나로 통일되지 않아 혼동하기 쉬운 모듈 시스템을 정리해보자.

똑같은 js 파일이어도, 웹 브라우저(클라이언트) 에서 사용하는 모듈 시스템과
Node.js(서버) 에서 사용하는 모듈 시스템은 다르다.

예를 들면 다음과 같은 경우 node.js 에서는 정상동작하지만
브라우저에서 실행시키면 not defined 에러가 발생한다.

    <script type="text/javascript">
        const childProcess = require('child_process');
        childProcess.execFile('./a.txt');
    </script>

Uncaught ReferenceError: require is not defined

CommonJS Module

require, module.exports, exports 키워드를 사용한다.

const express = require('express');
const router = express.Router();

// ... (중략)
module.exports = routes;

모듈을 동기적으로 로드한다. 👉 브라우저에는 적합하지 않다.
CommonJS를 브라우저에 사용하기 위해서 Browserify라는 컴파일러 툴을 사용한다.

ES6 Module

import, export 키워드를 사용한다.

import {Router} from 'express';
export default routes;

CommonJS 와 가장 큰 차이점은 import가 컴파일 시점에 일어난다는 것이다. (인터프리터로 코드를 직접 실행하기 전에)
=> 실제로 사용될 모듈만 로딩함으로써 메모리를 절약하고 브라우저에 가해지는 부하를 줄인다.
이를 'elminitation dead code' 라고한다.

킹치만 아무데서나 ES Module 사용이 가능한건 아닌걸?!?!


⚠️ Node.js 구버전에서 ES Module 사용시 다음과 같은 에러 메시지를 만나게된다.

{
    "errorType": "Runtime.UserCodeSyntaxError",
    "errorMessage": "SyntaxError: Cannot use import statement outside a module"
}

한마디로 "너 왜 ES Module 쓰냐 기본적으로 CommonJS 인데 ㅋㅋ" 라고 노드가 말해주는 것이다.

ES Module , 13~14 버전 노드에서 쓰고싶다면?

package.json 파일에 최상단 "type": "module" 만 입력해주면 된다.

그러나...

"require() of ES module is not supported"

AWS Labmda에서 이벤트 핸들러 함수를 호출할 때 기본적으로 require()를 사용하고 있다. (CommonJS)

ES6 모듈 시스템.. 아직 표준이 아니야 😭

Experimental.. 노드진영에서 아직도 ECMAScript를 표준으로 지정하지 않고
CommonJS와 ES6를 둘다 지원하고있다.

따라서 추후 하나의 모듈 시스템을 표준으로 삼을 수 있기 때문에 package.json 파일에 다음과 같이 어느 모듈시스템을 사용할 것인지 explicit 하게 명시하길 권장하고있다.

ECMAScript Modules

// package.json
"type": "module"

CommonJS

//package.json
"type": "commonjs"

드디어.. Node v15.x 부터는 안정화된 버전으로 ES Module을 사용할 수 있게되었다.

아직 안심하긴 이르다. 2021년 2월 23일 기준 Node.js의 LTS버전은 14.x에 머물러있기 때문이다.


2022-07-27 tsconfig/bases 에서 TSConfig 추천 세팅을 간단하게 끌어올 수 있다. 현재 AWS Lambda 에서도 Node.js 16.x 버전을 지원한다.

install

# Use either
$ npm install --save-dev @tsconfig/node16-strictest-esm
$ yarn add --dev @tsconfig/node16-strictest-esm

Add to your tsconfig.json

"extends": "@tsconfig/node16-strictest-esm/tsconfig.json"

node16/nodenext (nightly builds)
Available from 4.7+, the node16 and nodenext modes integrate with Node’s native ECMAScript Module support. The emitted JavaScript uses either CommonJS or ES2020 output depending on the file extension and the value of the type setting in the nearest package.json. Module resolution also works differently. You can learn more in the handbook.
-Typescript org

=> 이제 Native Node.js 가 .cjs , .mjs 파일 확장자에 따라 알아서 import export 하면서 실행해줄 것이다.

Webpack

Webpack provides some useful features like 'code splitting'
코드 쪼개기? 뭔소리냐?

CommonJS로 작성된 패키지와 ES6 로 작성된 패키지를 짬뽕해서 사용한다면
require import export 가 뒤섞여 모듈시스템이 통일되지 않아 아주 난리 부르스가 날 것이다. (탈모 올거같아 이거 때문에)

ES Module를 기본으로 사용하고 특정 라이브러리만 CommonJS 로 로드하는 상황이 있다고 가정하면
필요한 경우 (해당 라이브러리를 끌어다 쓰는)에만 로딩될 수 있도록 하는 것이다.

결론

대부분의 서비스에서 ES의 최신 모듈을 사용하고 싶다면 babel 혹은 TypeScript 를 사용해서 트랜스파일링 한 뒤 배포하는게 상책이다.

프론트의 경우 IE같은 구형브라우저는 Class 객체 문법을 일체 지원하지 않으므로 더더욱이 트랜스파일링이 필요하다.

참다참다 결국 나도 타입스크립트로 갈아탔다. 아!!


Reference

JS Module bundling

profile
I'm still hungry

0개의 댓글