node.js nest.js 의 환경에는 commonJs, ESM이라는 모듈 참조방식 2가지가 있습니다.
이는 Node.js 환경에서 개발을 하려면 알아야하는 방식인데 해당 내역들을 자세히 알아보겠습니다.
CommonJS
CommonJS는 JavaScript를 서버 사이드(Node.js)나 데스크톱 환경 등 브라우저 외의 환경에서 사용할 수 있도록 모듈 시스템을 표준화하려는 프로젝트 및 사양입니다. CJS의 핵심은 모듈을 동기적(Synchronous)으로 로드하여, 파일에서 필요한 모듈을 즉시 가져와 사용할 수 있도록 하는 것입니다.
등장 배경 및 시기
- 등장 시기: 2009년경 (원래 이름은 ServerJS였음)
- 목표: 당시 웹 브라우저 외의 환경(서버, 명령줄 도구)에서 JavaScript를 사용할 때 표준화된 모듈 방식이 없었기 때문에, 파일 시스템, I/O, 모듈 로딩 등을 위한 공통의 API와 사양을 만들기 위해 시작되었습니다.
- 주요 채택: Node.js가 이 CommonJS 사양을 채택하고 구현함으로써, Node.js 생태계의 모듈 시스템으로 자리 잡았습니다. 따라서 Node.js에서 사용하는 require()와 module.exports가 바로 CJS의 구현체입니다.
장단점
- 장점
- Node.js 기본 모듈 시스템이기 때문에 호환성 좋음
- 대부분의 Nest.js 예제, 라이브러리, 타사 패키지가 CommonJS 기반
- 설정이 단순하고 기존 Node.js 프로젝트와 통합 쉬움
- 단점
- Tree-shaking 불가 → CJS의 require() 문은 코드 실행 시점에 동적으로 모듈을 로드합니다. 번들러가 코드 실행 전에 require가 무엇을 가져올지 정확히 예측할 수 없기 때문에, 코드를 안전하게 제거하기 어렵습니다.
- ESM 전용 라이브러리 사용 시 import로 불러오기 어려움
트리 쉐이킹(Tree Shaking)은 JavaScript 생태계에서 사용되는 코드 최적화 기술 중 하나로, 실제로 사용되지 않는(Dead Code) 코드를 최종 빌드 결과물에서 제거하여 애플리케이션의 크기를 줄이는 기법
CommonJS 의 내보내기 및 가져오기
1. 내보내기 (Export)
CommonJS 모듈(CJS)은 본질적으로 Named Export와 Default Export를 구분하는 ESM의 개념이 없습니다. CJS는 단순히 하나의 객체를 module.exports로 내보냅니다.
문법 역할
| module.exports = value; | 모듈의 대표 값을 통째로 내보냅니다. (Default Export와 유사) |
| module.exports.name = value; | 내보낼 객체에 속성을 추가하여 여러 값을 내보냅니다. (Named Export와 유사) |
코드
// module.js (CJS)
const MyFunc = () => 'Hello CJS';
const MyData = 100;
module.exports = {
MyFunc,
MyData,
};
2. 가져오기 (Import)
require() 함수를 사용하여 module.exports 객체 전체를 동기적으로 가져옵니다.
문법 역할
| const module = require('./file.js'); | 내보낸 객체 전체를 가져와 변수에 할당합니다. |
| const { name } = require('./file.js'); | 가져온 객체에서 원하는 속성만 구조 분해 할당(Destructuring)을 통해 가져옵니다. |
코드
// app.js
const myModule = require('./file');
console.log(myModule.VERSION); // '1.0'
// 원하는 값만 가져올 경우
const { add } = require('./file');
console.log(add(1, 2)); // 3
ECMAScript Module (ESM)
ECMAScript Module은 JavaScript 언어의 공식 표준을 정의하는 ECMAScript(ES) 사양에 내장된 모듈 시스템입니다. CJS와 달리 비동기적(Asynchronous) 로드 방식을 지원하며, 정적 구조를 가지는 것이 특징입니다.
등장 배경 및 시기
- 등장 시기: ECMAScript 2015 (ES6) 사양에 공식적으로 포함되어 표준화되었습니다.
- 목표: JavaScript가 언어 자체적으로 표준화된 모듈 시스템을 가지지 못했던 문제와, 브라우저 환경에서 느린 네트워크 속도로 인해 모듈을 비동기적으로 효율적으로 로드해야 할 필요성 때문에 개발되었습니다.
- 주요 채택:
- 모든 현대 웹 브라우저에서 지원됩니다.
- Node.js도 최신 버전에서 이 ESM을 공식 모듈 시스템으로 지원하고 있으며, CJS와 함께 사용할 수 있습니다.
장단점
- 장점
- 최신 JavaScript 표준 문법 사용 가능
- Tree-shaking 지원 → ESM의 정적 특성: ESM의 import 및 export 문법은 코드 실행 전에 모듈 간의 의존성 관계를 미리 분석할 수 있습니다. 즉, 어떤 파일에서 무엇을 가져오는지 코드 실행 없이도 알 수 있습니다.
- 향후 Node.js와 프론트엔드 통합, Vite/Next.js 같은 ESM 친화적 환경과 호환
- 단점
- CommonJS 기반 라이브러리 import가 까다로움
- __dirname, __filename 등 Node 전역 변수 사용 시 추가 설정 필요
- Nest.js 공식 문서 예제는 대부분 CommonJS 기준 → 초기 설정 복잡
ESM의 내보내기 및 가져오기
1. 내보내기 (Export)
ESM은 import와 export 키워드를 사용하며, Default Export와 Named Export를 명확히 구분합니다.
문법 역할
| export default value; | 모듈의 대표 값을 내보냅니다. (모듈당 하나만 가능) |
| export const name = value; | 이름이 지정된 값을 내보냅니다. (여러 개 가능) |
코드
// file.js
export const VERSION = '1.0'; // Named Export
export const subtract = (a, b) => a - b; // Named Export
export default function multiply(a, b) { // Default Export (하나만 가능)
return a * b;
2. 가져오기 (Import)
import 문법을 사용하여 모듈을 가져옵니다.
문법 역할
| import name from './file.js'; | Default Export 값을 가져옵니다. (원하는 이름으로 지정 가능) |
| import { name } from './file.js'; | Named Export 값을 가져옵니다. (이름을 정확히 일치시켜야 함) |
| import * as name from './file.js'; | 모든 내보내기를 객체 하나로 묶어(Namespace) 가져옵니다. |
| (사용 지양), 반드시 필요할 때만 사용. |
코드
// app.js
import multiplyFunc from './file'; // Default Export 가져오기
import { VERSION, subtract } from './file'; // Named Export 가져오기
console.log(multiplyFunc(2, 3)); // 6
console.log(VERSION); // '1.0'
CJS와 ESM의 차이
구분 CommonJS (CJS) ECMAScript Module (ESM)
| 주요 문법 | require() 및 module.exports | import 및 export |
| 로드 방식 | 동기적(Synchronous) | 비동기적(Asynchronous) |
| 적용 환경 | Node.js의 전통적인 방식 | 브라우저(표준), Node.js (최신 방식) |
| 실행 시점 | 런타임 (코드 실행 중 require() 호출 시) | 컴파일/파싱 시점 (코드 실행 전 모듈 구조 확정) |
| 특징 | require()는 조건문 내에서 사용 가능 (동적 로드) | import는 파일 최상단에서만 사용 가능 (정적 구조) |
| Default Export | 원래 개념 없음. module.exports 객체가 대체 | 공식적으로 존재 (export default) |
모듈 방식 코드 번들러의 인식 트리 쉐이킹 가능 여부
| ESM | import { a } from './lib'; | 'a'만 사용됨을 정확히 파악 | 가능 |
| CJS | const lib = require('./lib'); | 'lib' 전체가 사용됨을 추정 | 불가능 또는 제한적 |
'TypeScript > Nest.js' 카테고리의 다른 글
| NestJS 라이프사이클 (2/3): 가드, 파이프, 인터셉터! 요청이 Controller를 통과하는 7단계 (0) | 2025.11.25 |
|---|---|
| Node.js tsconfig esModuleInterop allowSyntheticDefaultImports 설정 완벽하게 알아보기 (0) | 2025.11.21 |
| NestJS Swagger JWT 인증 설정 방법 | 전역 보안 적용 가이드 (2) | 2025.11.15 |
| Day.js 오류 해결 완벽 가이드 Cannot read properties of undefined (reading '$i') with ESM CommonJS (0) | 2025.11.12 |
| TypeScript + Swagger 개발 환경 자동화: 디버깅용 Swagger 자동 실행 (0) | 2025.11.11 |
