
자바스크립트 모듈 시스템: require
vs import
자바스크립트는 본래 단순한 웹 스크립팅 언어로 시작했습니다. 그러나 애플리케이션의 규모가 점차 커지면서 코드의 재사용성을 높이고, 의존성을 관리하며, 변수명 충돌을 방지할 수 있는 체계적인 모듈 시스템의 필요성이 커졌습니다. 이러한 배경 속에서 CommonJS의 require
와 ECMAScript Modules(ESM)의 import
라는 두 가지 주요 모듈 시스템이 등장했습니다.
등장 배경과 특징
require
(CommonJS)
require
는 주로 2009년경 Node.js가 등장하며 서버 측 자바스크립트 개발의 표준으로 자리 잡았습니다. Node.js는 파일 시스템에 즉시 접근할 수 있기 때문에 모듈을 동기적으로 불러오는 require
방식이 매우 적합했습니다. 이 방식은 require
호출이 완료될 때까지 다음 코드가 실행되지 않게 하여, 서버 환경에서 예측 가능한 코드 흐름을 보장합니다.
import
(ES Modules)
import
는 ECMAScript 2015(ES6) 표준의 일부로 공식화된 자바스크립트 언어 자체의 모듈 시스템입니다. 기존의 비공식 모듈 시스템이 가진 한계를 극복하고 언어 차원의 통일된 모듈 기능을 제공하기 위해 설계되었습니다. import
는 웹 브라우저 환경을 고려하여 모듈을 비동기적으로 불러올 수 있도록 설계되었습니다. 이 비동기 특성 덕분에 네트워크를 통해 모듈을 가져와야 하는 브라우저에 최적화되어 있습니다. 또한, import
는 정적 분석이 가능해 번들링 도구와 같은 개발 도구들이 코드 의존성을 효율적으로 파악하고, 사용되지 않는 코드를 제거하는 트리 쉐이킹(Tree Shaking) 같은 최적화 기술을 적용하는 데 매우 유리합니다.
모듈 사용 방법
require
(CommonJS)
CommonJS에서는 module.exports
를 사용하여 모듈의 기능을 외부에 공개하고, require()
함수로 모듈을 가져옵니다. 명시적인 기본(default) 내보내기나 이름(named) 내보내기 개념은 없으며, module.exports
에 할당된 값이 모듈 전체의 단일 내보내기가 됩니다.
1. 모듈 내보내기 (module.exports
와 exports
)
module.exports
객체 할당 방식: 여러 함수나 값을 하나의 객체로 묶어 내보낼 때 사용합니다. 이 방식은module.exports
의 기존 값을 완전히 덮어씁니다.
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
exports
객체 속성 추가 방식:exports
는module.exports
를 참조하는 변수입니다. 여러 개의 속성을 개별적으로 내보낼 때 편리합니다.
// calculator.js
const multiply = (a, b) => a * b;
export default multiply;
2. 모듈 가져오기 (require()
)
require()
함수는 module.exports
로 내보낸 객체나 값을 반환합니다.
// app.js
const math = require('./math');
console.log(math.add(5, 3)); // 8
const { multiply, divide } = require('./calculator');
console.log(multiply(4, 5)); // 20
import
(ES Modules)
ES Modules에서는 export
와 import
키워드를 사용합니다. 기본 내보내기(default export)와 이름 있는 내보내기(named export)를 명확하게 구분하는 것이 특징입니다.
1. 모듈 내보내기 (export
)
- Named Exports (이름 있는 내보내기): 여러 함수, 변수, 클래스 등을 각각의 이름으로 내보냅니다. 가져올 때도 동일한 이름을 사용해야 합니다
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
- Default Export (기본 내보내기): 모듈당 단 하나만 내보낼 수 있는 방식입니다. 모듈을 가져올 때 원하는 이름으로 자유롭게 지정할 수 있습니다.
// calculator.js
const multiply = (a, b) => a * b; export default multiply;
2. 모듈 가져오기 (import
)
- Named Imports (이름으로 가져오기):
export
된 특정 요소를 선택적으로 가져옵니다.as
키워드를 사용해 별칭을 부여할 수도 있습니다.
// app.js
import { add } from './math.js';
console.log(add(5, 3)); // 8
import { subtract as minus } from './math.js';
console.log(minus(10, 4));
// 모든 named export를 하나의 객체로 가져올 수도 있습니다
import * as MathFunctions from './math.js';
console.log(MathFunctions.add(2, 2)); // 4
- Default Import (기본 가져오기):
default export
된 모듈을 가져옵니다. 중괄호{}
없이 원하는 이름을 지정합니다.
// app.js
import calculate from './calculator.js';
console.log(calculate(4, 5)); // 20
Node.js에서의 package.json
설정
Node.js 환경에서 두 모듈 시스템을 명시적으로 사용하려면 package.json
파일의 type
필드를 설정해야 합니다.
CommonJS 프로젝트
기본적으로 package.json
에 type
필드가 없거나 "type": "commonjs"
로 설정되면 Node.js는 해당 프로젝트를 CommonJS 모듈로 간주합니다.
// package.json (CommonJS 기본)
{
"name": "my-commonjs-app",
"type": "commonjs", // 이 필드가 없어도 동일하게 작동
"main": "index.js"
}
ES Modules 프로젝트
Node.js에서 ES Modules를 사용하려면 package.json
에 "type": "module"
을 추가해야 합니다. 이 설정은 해당 패키지 내의 .js
파일을 ES Modules로 해석하게 합니다.
JSON
// package.json (ES Modules 설정)
{
"name": "my-esm-app",
"type": "module", // ES Modules 사용을 명시
"main": "index.js"
}
확장자 규칙:
.js
:package.json
의type
설정에 따라 CommonJS 또는 ES Modules로 동작합니다..mjs
:type
설정에 관계없이 항상 ES Modules로 해석됩니다..cjs
:type
설정에 관계없이 항상 CommonJS로 해석됩니다.
현재 자바스크립트 생태계는 ES Modules를 표준으로 채택하고 있으며, 대부분의 환경에서 ES Modules로의 전환이 이루어지고 있습니다. 하지만 기존 프로젝트나 특정 환경에서는 여전히 CommonJS가 사용될 수 있어 두 시스템의 차이를 이해하는 것이 중요합니다.