본문 바로가기
📌 Front End/└ Node.js

[Node.js] Nodejs 모듈(CommonJS / ES Modules)

by 쫄리_ 2024. 8. 9.
728x90
반응형

모듈(Module) 이란?

분리된 하나의 파일을 모듈(module)이라고 부르는데, 

모듈은 대게 클래스 하나 혹은 특정한 목적을 가진 여러개의 함수 

포함하는 라이브러리로 구성되어 있다.


모듈 시스템

  • CommonJS -  NodeJS 환경을 위해 만들어진 모듈 시스템
  • ES Module - ES6(ES2015)에 도입된 자바스크립트 모듈 시스템
특징 CommonJS ES6 모듈
확장자 .js .cjs .js(package.json에 "type":"module" 추가)
.mjs
로딩 방식 동적 로딩 (런타임) 정적 로딩 (컴파일 타임)
문법 require, module.exports import, export
모듈 범위 파일 단위 파일 단위
트리 셰이킹 지원하지 않음 지원 (최적화 가능)
사용 환경 주로 서버 (Node.js) 브라우저, 서버 (트랜스파일러)
엄격 모드 선택적 적용 자동 적용

 


📌 최초의 모듈 시스템 (JavaScript 모듈 시스템)

최초에는 script 태그로 자바스크립트 파일을 삽입하면 브라우저에서 순서대로 로드 하는 방식 이었습니다.

<html>
  <script src="/foo.js"></script>
  <script src="/bar.js"></script>
</html>

 

🔎 문제점

모듈 간 스코프 구분이 되지 않습니다. 

즉, foo 에 있는 변수명과 bar에 있는 변수명이 겹치면 잘못 동작하게 됩니다.
모듈 순서를 신경쓰면서 개발해야 합니다.

 

💡 해결책

자바스크립트를 모듈화 하여 서로 간섭하지 않게 만들자.

이에 따라 자바스크립트를 모듈화 하기 위한 새로운 기술 등장 했습니다.

 


📌 CommonJS 모듈 시스템

Node.js 환경에서 실행되는 JavaScript는 모듈 시스템으로서 CommonJS 방식을 지원한다.

이 방식에서는 module.exports 객체를 이용하여 자신의 데이터를 외부로 내보낼 수 있고,

require() 함수를 이용하여 외부 모듈의 데이터를 불러올 수 있다.

 

만약 Babel 등의 컴파일러를 사용한다면 뒤에서 설명할 ES6 기반의 모듈 내보내기 및 불러오기 방식을 사용해도

알아서 module.exports 객체 및 require() 함수 기반의 방식으로 변환될 것이다.


📁 파일 확장자

CommonJS는 기본적으로 .js 확장자를 사용합니다.


📤 모듈 내보내기 (module.exports)

  1. 여러개의 객체를 내보낼 경우  → exports.변수/함수명 의 개별 속성으로 할당
  2. 딱 하나의 객체를 내보낼 경우 → module.exports = 객체 자체에 할당

module.exports 또는 exports 객체를 사용하여 변수와 함수 모듈을 내보냅니다.

자신의 데이터를 외부로 내보내려면 

module.exports 변수에 내보내고자 하는 데이터들을 담은 객체를 지정해주면 된다. 

그러면 이것을 불러오는 쪽에서는 해당 객체에 접근하여 내보내진 데이터들을 사용할 수 있다. 

참고로 exports라는 변수도 존재하는데, 이는 module.exports 객체를 가리킨다. 

따라서 module.exports의 프로퍼티를 수정하든 exports의 프로퍼티를 수정하든 효과는 같다. 

그러나 exports 자체에 다른 값을 대입하는 것은 안 된다. 

더 이상 module.exports 객체를 가리키지 않게 되기 때문이다. 

실제로 내보내지는 것은 module.exports 객체이다.

module.exports = 128;

module.exports = { name: '홍길동', height: 180 };

exports.name = '홍길동';

module.exports = function() { /* 코드 */ };
// ▶ 여러개를 내보내기 위해서는 오브젝트형으로 묶어서 반환
function A(){
    console.log('A함수');
}
function B(){
    console.log('B함수');
}

module.exports = {
    A, B
};

📥 모듈 불러오기 (require)

동기식으로 동작합니다.

외부 모듈의 데이터를 불러오려면 require("경로") 함수의 반환 값을 변수에 대입하면 된다. 

require() 함수가 반환하는 것은 해당 모듈의 module.exports 객체이다.

const obj = require("./currency-object");  // obj는 module.exports 객체를 가리킨다.

⚙️ 환경설정

CommonJS는 Node.js의 기본 모듈 시스템입니다. 별도의 설정 없이 사용할 수 있습니다.

 


📌 ECMAScript 모듈(ESM) 시스템

앞서 배경 설명에서 언급했듯이,

이는 브라우저 단에서도 쉽게 JavaScript의 모듈화가 가능하도록 ES6부터 도입된 방식이다.

모듈화 시스템답게 각각의 모듈(파일)마다 독립적인 파일 스코프를 가지고 있어서,

모듈 내에 var로 선언한 변수는 더 이상 window 객체의 프로퍼티가 아닌 파일 스코프의 변수로 존재하게 된다.

즉 기본적으로는 다른 모듈의 데이터를 참조할 수 없기 때문에 충돌도 발생하지 않는다.

 

이때 다른 모듈의 데이터를 참조하거나 자신의 데이터를 노출시키고 싶을 때 사용하는 것이

바로 export, import 키워드이다.

export 키워드로 자기 자신의 데이터를 외부로 내보낼 수 있고,

import 키워드로 외부 모듈의 데이터를 불러올 수 있다.

이러한 모듈 시스템을 브라우저에서 사용하려면 <script> 태그에 type="module" 어트리뷰트를 추가해야 한다.

그러면 그 안에 작성된 JavaScript 코드들은 ES6 기반의 모듈 내보내기 및 불러오기 방식을 지원하게 된다.

이때 불러오는 파일이 모듈임을 명확히 하기 위해 <script type="module"> 태그로 불러오는

JavaScript 파일의 확장자는 mjs로 설정하도록 권장되고 있다.

ES6 기반의 모듈 시스템은 CommonJS 방식에 비해 코드의 직관성이 좋고, 

비동기 방식으로 작동하면서 불러오는 모듈의 실제로 사용되는 부분들만 로드하기 때문에 

성능적으로도 효율적이라고 할 수 있다. 

그러나 이는 아래와 같은 단점들을 가지고 있어서 아직까지는 Webpack 등의 모듈 번들러를 이용하여 

미리 의존성이 해결된 형태의 번들 JavaScript 파일을 제공하는 방식이 더 선호되는 경향이 있다.


📁 파일 확장자

기본적으로 .mjs 확장자를 사용하거나, 

package.json 파일에 "type": "module"을 추가하여 .js 파일을 ESM으로 사용할 수 있습니다.


📤 모듈 내보내기 (export)

  • Named Export : 정해진 이름으로 내보내기
export 변수/함수/클래스 선언문;

export { 변수명/함수명/클래스명 };

export { 변수명/함수명/클래스명 as 다른 이름 };
// ▶ 추가 내보내기는 as를 통해 이름 변경 가능
function B(){
    console.log('B함수');
}
function C(){
    console.log('C함수');
}

export {B as FuncB, C};

 

  • Default Export : 기본 내보내기 (이름을 정하지 않음. 최대 하나만 가능)
export default 선언문 또는 값;

export { 변수명/함수명/클래스명 as default };

📥 모듈 불러오기 (import)

비동기식으로 동작합니다.

// ▶ A는 Default Export, B와 C는 Named Export
import A, { B, C } from 경로;  

// ▶ 원하는 이름으로 로드
import { B as b, C as c } from 경로;  

// ▶ Export 된 모든 것들을 하나의 객체 형태로 로드 
//    (불필요한 것도 가져오면 번들링 시 비효율을 야기)
import * as obj from 경로;  

// ▶ "import A from 경로"와 동일 
//    (default : Default Export를 참조하는 용도로 사용하는 키워드)
import { default as A } from 경로;

⚙️ 환경설정

ESM을 사용하려면 

package.json 파일에 "type": "module"을 추가하거나 

.mjs 파일 확장자를 사용해야 합니다.

{
   "type": "module"
}

 


🗃️ 모듈 전체 내보내기 / 가져오기

📌 CommonJs 문법 (require / exports)

// 모듈 전체를 export, 파일내 한번만 사용가능하다.
const obj = {
   num: 100,
   sum: function (a, b) {
      console.log(a + b);
   },
   extract: function (a, b) {
      console.log(a - b);
   },
};

module.exports = obj;
const obj = require('./exportFile.js');

obj.num; // 100
obj.sum(10, 20); // 30
obj.extract(10, 20); // -10

📌 ES6 문법 (import / export)

// 모듈 전체를 export, 파일내 한번만 사용가능하다.
const obj = {
   num: 100,
   sum: function (a, b) {
      console.log(a + b);
   },
   extract: function (a, b) {
      console.log(a - b);
   },
};

export default obj;
// 전체 자체를 import 할 경우 중괄호 없이 그냥 씀
import obj from './exportFile.js';

obj.num; // 100
obj.sum(10, 20); // 30
obj.extract(10, 20); // -10

 


📁 모듈 개별 내보내기 / 가져오기

📌 CommonJs 문법 (require / exports)

// ▶ 멤버를 직접 일일히 export
exports.name = 'Ann'; // 변수의 export
exports.sayHello = function() {	// 함수의 export
    console.log('Hello World!');
}
exports.Person = class Person {	// 클래스의 export
    constructor(name){
        this.name = name;
    }
}
const { name, sayHello, Person } = require('./exportFile.js');

console.log(name);	// Ann
sayHello();		// Hello World!
const person = new Person('sohyun');

📌 ES6 문법 (import / export)

// ▶ 멤버를 직접 일일히 export
export const name = 'Ann';	// 변수의 export
export function sayHello() {	// 함수의 export
    console.log('Hello World!');
}
export class Person {		// 클래스의 export
    constructor(name){
        this.name = name;
    }
}


// ----------------------- OR ----------------------- //


// ▶ 멤버를 따로 묶어서 export
const name = 'Ann';
function sayHello() {
    console.log('Hello World!');
}
class Person {
    constructor(name){
        this.name = name;
    }
}
export {name, sayHello, Person}; // 변수, 함수, 클래스를 하나의 객체로 구성하여 export
import { name, sayHello, Person } from './exportFile.js';

console.log(name); // Ann
sayHello(); // Hello World!
const person = new Person('inpa');


// ----------------------- OR ----------------------- //


// 개별로 export 된걸 * 로 한번에 묶고 as 로 별칭을 줘서, 
// 마치 전체 export default 된걸 import 한 것 처럼 사용 가능
import * as obj from './exportFile.js'; 

console.log(obj.name); // Ann
obj.sayHello(); // Hello World!
const person = new obj.Person('inpa');

➕ 구조분해 할당

// ▶ 객체의 구조분해 할당
const obj = {
    a:3,
    b:4,
};

console.log(obj); // {a: 3, b: 4}
console.log(obj.a , obj.b); // 3 4

------------------------------------------

// ▶ 구조분해 할당
const {a,b} = obj // 키값을 동일시
console.log({a,b}); // {a: 3, b: 4}
console.log(a,b); // 3 4

------------------------------------------

// ▶ 배열의 구조분해 할당
const c = [3,4,5,6];
console.log(c); // [3,4,5,6]
const [x,y] = c;  
// 배열은 순서가 보장되어 있기 때문에 키값을 맞추어 주지 않아도 됨
console.log(x,y); // 3 4

 


728x90
반응형