node.js와 Common JS

📅 TIL #33



🎯 Achievement Goals


  • node.js에서 사용하는 package.json과 npm script에 대해 이해할 수 있다.
    • devDependencies와 dependencies의 차이점을 이해할 수 있다.
    • npm이 무엇인지, 모듈이 무엇인지 이해할 수 있다.


  • CommonJS의 도입 배경을 이해할 수 있다.
    • node.js에서 사용하는 require 문법 및 module.exports, exports에 대해 이해할 수 있다.
    • 모듈이 어떤 식으로 불리는 지 이해할 수 있다.




👉 node.js


package.json ?


node.js에서는 npm 모듈이라는 이름으로 부르며, 이에 대한 정보를 담아둔 곳이 바로 package.json 이다. 이 곳에 프로젝트(패키지) 전반에 관한 정보가 들어있다. 그렇다면 npm이란 무엇인가?

npm


Node Package Manager의 약자로 일종의 앱스토어라고 생각하면 된다. 모듈들이 모여있는 스토어의 개념이다.

// package.json

// 프로젝트에 관한 정보
{
  "name": "node.js_practice",
  "version": "1.0.0",
  "description": "",

  "main": "index.js",

// CLI에서 사용가능한 명령
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "echo \"Error: no test specified\" && exit 1"

  },
  "author": "useong",
  "license": "ISC",

// 개발과 관련된 dependency 들
  "dependencies": {
    "bcrypt": "^5.0.0",
    "body-parser": "^1.19.0",
    "cookie-parser": "^1.4.5",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^5.11.17"

  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}


package.json에는 이 프로그램을 실행시키기 위해 필요한 모듈들이 무엇인지, 프로그램을 실행시키는 방법, 프로그램 테스트하는 방법 등이 명시되어 있다.

헷갈리면 안되는 점은 이 프로그램을 실행시키기 위해 필요한 실제 모듈은 따로 node_modules 이라는 폴더에 저장된다는 것이다.

보통 처음에는 아무것도 없는 상태로 시작하기 때문에 가장 먼저 해야할 것은 npm을 다운로드 받아야 한다.


$ npm install

// shortcut
$ npm i



scripts 항목

// CLI에서 사용가능한 명령
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "echo \"Error: no test specified\" && exit 1"


스크립트 항목에는 다음과 같이 있다. 여기서 중요한 점은 스크립트 항목은 CLI에서 사용 가능한 명령어라는 점이다.

프로그램을 실행할 때에는 npm run 스크립트 이름 으로 실행한다.

위에 파일 같은 경우는 스크립트 이름이 start로 되어있기 때문에


$ npm run start

이렇게 입력해주면 프로그램을 실행시킬 수 있다.



Dependency


// 개발과 관련된 dependency 들
  "dependencies": {
    "bcrypt": "^5.0.0",
    "body-parser": "^1.19.0",
    "cookie-parser": "^1.4.5",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^5.11.17"

  },
  "devDependencies": {
    "nodemon": "^2.0.7",
    "mocha": "^8.2.0"
  }

위에 코드에는 dependenciesdevDependencies 가 있다.

각각 이 둘의 차이점은 무엇일까?

먼저 dependencies는 모듈들이라고 생각하면 된다. 특별히 개발이나 실행에 해당 모듈을 의존한다고 해서 의존성(dependency)에서 가져온 뜻이다.

JSON 형식에서 키 값은 모듈 이름이고, 값은 버전이다.


devDependencies안에는 프로젝트를 개발하는 환경에서 필요한 모듈들이 무엇인지 적혀 있다.

예를 들어 위에 같은 경우는 nodemon 이 적혀 있는데, 굳이 서버를 내리고 수정하고 다시 올리지 않아도 소스를 변경할 때 마다 그걸 감지해서 자동으로 서버를 재시작해주는 일종의 툴(Tool)이다.


즉, 실제로 프로젝트 동작에 직접적으로 영향을 주지 않는 모듈들을 명시한다.

nodemon 뿐만 아니라, 서버를 테스팅해주는 툴도 이에 해당한다.

devDependencies를 다운로드 받을 때는 아래와 같이 명령어를 입력한다


// 서버를 수정하면 재시작 하지 않아도 바로 확인할 수 있게 해주는 툴
$ npm install nodemon --save-dev

// 프로그램 테스팅 툴
$ npm install mocha --save-dev



왜 package.json에 명시해야 할까?


혼자 개발하고 혼자 프로그램을 실행시킨 다면 아무 상관 없을 것이다.

하지만 아무것도 모르는 사람이 내가 만든 프로젝트를 실행시킬 경우에는 어떻게 프로그램을 실행시킬 수 있는지, 어떤 모듈이 있는지 알 수 없기 때문이다.

즉, package.json은 다른 사람들에게 나의 프로그램을 알리기 위한 역할이라고 보면 된다.



👉 CommonJS

도입 배경


범용적인 목적으로 JavaScript를 사용하기 위해 필요한 선결 조건은 모듈화이다. Node.js도 이런 모듈화 작업때문에 탄생할 수 있었다고 한다. JavaScript 모듈화 작업의 선두 주자는 CommonJS와 AMD 두가지가 있다.

node.js 에서는 다른 모듈을 불러오기 위해 require 구문을 사용할 수 있다.


CommonJS 란?


CommonJSJavaScript를 브라우저에서뿐만 아니라, 서버사이드 애플리케이션이나 데스크톱 애플리케이션에서도 사용하려고 조직한 자발적 워킹 그룹이다.

CommonJSCommonJavaScript를 브라우저에서만 사용하는 언어가 아닌 일반적인 범용 언어로 사용할 수 있도록 하겠다는 의지를 나타내고 있는 것이라고 이해할 수 있다.



노드의 모듈 개념


노드에서 모듈이라는 개념은 노드로 개발한 애플리케이션을 이루는 기본 조각이라고 할 수 있다.

쉽게 말하면 물건을 만들 때 쓰는 부품같은 개념이다.미리 만들어진 파트들을 조립해서 물건을 만드는 것처럼, 관련된 코드들을 모아서 캡슐화해놓은 것을 모듈이라고 부른다. 그리고 여러 가지 모듈을 이용하면 프로젝트를 훨씬 더 수월하게 진행할 수 있다!!

스크린샷, 2021-02-25 00-57-51



노드의 모듈화 개념


노드는 확장성을 위해 모듈 구조를 통해 애플리케이션을 구성하도록 하고 있다. 모듈은 애플리케이션을 이루는 기본 단위로, 보통 다수의 클래스와 이를 통해 생성한 객체 등으로 구성된다.

이렇게 모듈 단위로 구성된다는 것은 객체 지향 컨셉으로 애플리케이션이 구성된다는 것을 의미한다고 말할 수 있다.

기본적으로 노드의 모듈은 자바스크립트 파일 하나와 1:1로 맵핑된다고 생각하면 된다. 이러한 모듈 형태는 노드 확장 모듈의 개발을 편리하게 해주며, 실제 우리가 애플리케이션을 개발할 때에도 소스 코드를 깔끔하게 구성할 수 있도록 해준다.

노드의 모듈은 CommonJS 모듈 스펙으로부터 많은 영향을 받아 구현되었지만 완벽하게 같진 않다고 한다. 하지만 많은 유사점이 있으며, CommonJS 기반의 다른 시스템과 공유하고자 한다면 이 스펙에 대해 이해하고 있는 것이 도움될 것이다.



require 와 module.exports


모듈 개념에서 require() 메소드와 module.exports가 왜 중요할까?

require() 메소드는 모듈 식별자인 module.exports를 이용해 모듈이 제공하는 함수 또는 객체 등을 반환환다. 만약 불러온 모듈이 다른 모듈이 있어야 한다면 그 모듈도 같이 로드하도록 되어 있다. 이것은 노드 모듈화에서 핵심 내용이다.

노드에서 하나의 자바스크립트 파일은 하나의 모듈이 된다. 이때 자바스크립트 파일 내부에서 함수나 변수를 module.exports에 할당하면 외부에서 접근할 수 있습니다.


간단한 문제1

// 파일 index.js
let x = 10;
let mod = require("./lib/useong-module.js");
let result = mod.x;

// 파일 lib/useong-module.js
let x = 20;
module.exports.x = 30

index.js가 실행되면 result의 값은 무엇이 될까?



정답은 30이다! 👏👏👏



간단한 문제2

// 파일 index.js
let x = 10;
let mod = require("./lib/useong-module.js");
let result = mod.x;

// 파일 lib/useong-module.js
let x = 20;

exports.x = 20
module.exports.x = 30

이번에는 result의 값이 어떻게 될까?



이번에도 정답은 30이다! 👏👏👏



중요 포인트는 ?


require 키워드는 object를 반환환다. 그리고 module.exportsexports 는 call by reference 로 동일한 객체를 바라보고 있고, 리턴되는 값은 항상 module.exports 이다.

모듈은 기본적으로 객체이고, 이 객체를 module.exports, exports 둘 다 모두 바라보고 있는데, 최종적으로 return 되는 것은 무조건 module.exports 라는 것이다.




👊 내일의 TIW(today I Will)

Modern JavaScript Koans