[node.js] 모듈 사용하기 (Common JS)

Hyebin·2021년 4월 9일
0

node.js

목록 보기
2/6
post-thumbnail

브라우저에서는 다른 js 파일을 불러올 때 html <script>태그를 이용해서 불러올 수 있었다.
node.js에는 html이 없는데 js 파일에서 다른 js 파일이나 모듈을 어떻게 가져다 쓸 수 있을까

node.js modules

node.js에서 모듈은 부품이다. 애플리케이션에 포함될 어떤 기능을 갖는 것의 집합(함수, 파일 등)이 모듈이다.

외장 모듈

  • 개발자들이 만들어 놓은 것
  • npm으로 다운 받은 후 require로 사용한다.

내장 모듈

  • Node.js 설치시 그 안에 제공되는 모듈

모듈 불러와 사용하기

const 모듈이 담기는 변수명 = require('모듈이름')

node_modules에 저장되어 있는 모듈을 require('모듈이름')으로 불러올 수 있다.

node.js에는 기본적으로 포함하고 있는 내장 모듈들이 있다.
그 중 하나로 파일 시스템과 관련된 모듈인 File System module을 사용하려면 아래와 변수 fs에 fs를 담으면 된다.

const fs = require('fs') 

다른 js 파일 불러올 때

다른 js 파일을 불러오기 위해 require을 사용할 수도 있다.

// script1.js
const module2 = require('./script2.js')

// script2.js
console.log('this is module 2');

모듈을 노출시킬 때

내가 만들고자 하는 모듈을 js파일로 만들어 module.exports 라는 것을 써서 다른 js 파일에 넘겨 줄 수 있다.
만들어진 모듈은 require로 불러서 사용할 수 있다.

아래 예시를 보면 script2.js파일에서 'this is module 2'를 module.exports로 넘겨주었다.
script1.js 파일에서 require()로 script2.js를 불러와 모듈을 사용할 수 있게 된다.

// script1.js
const module2 = require('./script2.js')
console.log(modules2) // 'this is module 2'
// script2.js
module.exports = 'this is module 2'

이러한 모듈 노출 방법과, 불러오는 방법을 CommonJS 모듈 시스템이라고 부른다.

Common JS?

웹 브라우저 밖(서버사이드 어플리케이션, 데스크톱 애플리케이션)의 자바스크립트를 위한 모듈 생태계의 규칙을 설립하기 위한 프로젝트이다.

Common JS 공식사이트

Common JS 더 알아보고 싶다면 아래 사이트 글 읽어보기
JavaScript 표준을 위한 움직임: CommonJS와 AMD

모듈의 핵심은 모듈을 만들어서 바깥으로 내보내서 가져다 쓸 수 있는가이다. Common JS의 주요 명세도 모듈을 어떻게 사용할 것인가에 관한 것이다.

  • 모든 모듈은 자신만의 독립적인 실행 영역이 있어야한다.

  • 모듈 정의는 전역객체인 exports 객체를 이용한다.
    모듈을 사용할지 안할지 분리하는 작업을 위해 exports를 써서 구분한다.

  • 모듈 사용은 require 함수를 이용한다.

    module.exports VS exports

    module.exports, exports 둘 다 모듈을 만들어 내보내는 방법이다.

    module.exports가 오리진이다.

    exports는 일종의 축약형(shortcut)이다. 따라서 exports는 module.exports를 참조한다.
    exports라는건 module.exports를 가르키고 있는 변수인셈이다.

    let exports = module.exports = {}
// hello.js
exports.anything = function() {
  console.log('I am anything');
};

// hello-runner.js
const hello = require('./hello');

console.log(hello);	//{anything: Function}
hello.anything();	//I am anything
// hello.js
module.exports.anything = function() {
  console.log('I am anything');
};

// hello-runner.js
const hello = require('./hello');

console.log(hello);	//{anything: Function}
hello.anything();	//I am anything

만약 module.exports와 exports에 객체를 할당해줬을 때 둘의 차이가 있나? ⭕️

module.exports와 exports 둘 다 모듈을 만들어 내보내는 역할을 하므로 아래 두 개의 코드는 언뜻보기엔 이상이 없어 보이지만 require()로 불러와 출력해보면 다른 결과를 가진다.

//hello.js 
module.exports = {a: 2}

//hello2.js
const hello = require('./hello');
console.log(hello);	//{a:2}
//hello.js 
exports = {a: 2}

//hello2.js
const hello = require('./hello');
console.log(hello);	//{}

exports에 직접 참조하는 패턴을 쓰게되면 나타나는 문제이다. 이런 현상이 발생한 이유는 변수에 객체의 주소를 넣어주게 된 것이라 import를 하면 빈 객체만 출력이 되게 된다.
모듈화해서 exports에 직접 넣어주게 되면 변수에 할당한거지 module.exports에 넣어주는게 아니다.

예제로 이해하기

1) subject.js가 실행되면, result의 값?

//subject.js 파일
let x = 10
let mod = require('./lib/my-module.js')
let result = mod.x

//lib/my-module.js 파일
let x = 20
expots.x = 30

mod는 {x:30}, result 값은 30이 된다.
require()은 module.exports 값을 리턴값으로 받기 때문에 my-module.js을 읽어 exports된 값만 받아온다.

2) subject.js가 실행되면, result의 값?

//subject.js 파일
let mod = require('./lib/my-module.js')
let result = mod.x

//lib/my-module.js 파일
let x = 10
expots.x = 20
module.expots.x = 30

mod는 {x:30}, result 값은 30이 된다.
expots.x로 module.expots.x가 20이였는데, module.expots.x=30으로 x값이 재할당되어 들어갔다.

3) subject.js가 실행되면, result의 값?

//subject.js 파일
let mod1 = require('./lib/my-module.js');
let mod2 = require('./lib/my-module.js');
let result = (mod1 === mod2);

//lib/my-module.js 파일
expots.obj = { name: "Alice" };
```js
 result는 true이다.  
  mod1, mod2 둘다 똑같은 객체의 주소를 바라보고 있기 때문이다. 
  
### 4) subject.js가 실행되면, result의 값?
```js
//subject.js 파일
let mod1 = require('./lib/my-module.js');
let mod2 = require('./lib/my-module.js');
mod1.increment();
let result = mod2.increment();

//lib/my-module.js 파일
let counter = 0;
expots.increment = function () {
counter += 1;
return counter;
};

result값은 2이다.
mod1, mod2 둘 다 같은 객체의 주소를 가지고 있어 increment() 함수를 실행시 my-module.js increment에 할당된 함수가 실행되어 실행시마다 counter 값이 1씩 증가된 값을 리턴한다.

5) subject.js가 실행되면, 몇번의 console.log가 찍히나?

//subject.js 파일
let mod1 = require('./lib/my-module.js');
let mod2 = require('./lib/my-module.js');

//lib/my-module.js 파일
console.log("Loading module!")

mod1에서 읽어올 때 1번, mod2에서 읽어올 때 1번해서 2번 찍힐 줄 알았는데 1번만 찍히게 된다.

이유?
require에는 cache라는게 있어서 캐싱된 값을 그대로 사용하기 때문에 두번 부르지 않고, cache에 저장한 값을 사용한다. 그래서 처음 한번만 부르게 되고, 다시 부를 때는 캐싱된 값을 쓴다.

🚫 두번 부르고 싶다면?
require.cache를 초기화시켜주면 되는데 이건 해킹이라 본다. 원래 node.js 만드는 사람의 취지에 맞이 않으니 이건 사용하지 말고 참고만.

0개의 댓글