요약
- 모듈화가 잘 된 코드는 재사용성과 확장성을 높여 새로운 기능을 개발하거나 유지보수 할 때, 전체적인 비용을 감소시키고 개발팀의 생산성을 증대시킬 수 있습니다.
- Node.js의 모듈 시스템(module system)은 Node.js를 이루고 있는 가장 근본적이고 중요한 근간입니다.
- CommonJS는 Node.js의 첫 번째 내장 모듈 시스템으로 CommonJS의 모듈 명세를 따르고 있습니다.
- CommonJS의 모듈 명세는 모듈을 어떻게 정의하고, 어떻게 사용할 것 인지에 대한 것으로 모듈화에 필요한 것은 크게 세 가지입니다.
- 첫 번째는 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 한다는 것입니다.
- 두 번째는 모듈을 외부에서 사용할 수 있도록 공개하는 것인데, Node.js (CommonJS의 모듈 명세를 따르는)에서는 module.exports라는 전역 객체를 이용해서 정의합니다.
- 마지막으로 모듈을 사용하는 영역에서는 require() 함수를 이용해서 모듈을 불러옵니다.
- fs 모듈을 사용해서 파일 시스템에 접근할 수 있습니다.
- fs.readFileSync() 메서드는 동기적으로 실행되고, fs.readFile() 메서드는 비동기적으로 실행됩니다.
- npm은 Node.js 기반의 다양한 패키지를 설치하고 버전을 관리할 수 있게 도와주는 패키지 관리 도구 입니다.
모듈 = LEGO 블록
LEGO 블록: 독립적 존재, 사용자의 조립방법과 블록 종류에 따라 여러 결과물될 수 있음
모듈: 코드의 조각, 여러 모듈을 모아 하나의 소프트웨어가 됨
but 초기 JS는 모듈화X => ES6 이후 모듈화!
: JS를 브라우저, 서버side애플리케이션, 데스크톱 애플리케이션에서도 사용할 수 있게 명세(Specification)
를 만든 그룹.
Node.js는 이런 모듈화 작업의 결과물
명세
: 모듈을 어떻게 정의하고, 어떻게 사용할 것 인지.
1. 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 함
= 전역변수(Global variable)와 지역변수(Local variable)의 분리.
예) 서버 사이드 JavaScript: 파일마다 독립적인 파일 스코프가 있어, 파일 하나에 모듈 하나를 작성하는 방법으로 독립적인 실행 영역 구현.
2. 모듈을 외부에서 사용할 수 있도록 공개.
Node.js (CommonJS의 모듈 명세를 따르는)에서는 exports
라는 전역 객체를 이용해서 정의.
3. 모듈을 사용하는 영역에서는 require()
함수를 이용해서 모듈을 불러옴.
모듈은 파일과 1대1 대응 관계, 하나의 모듈은 자신만의 독립적인 실행영역(스코프)를 가짐.
module.exports
, exports 객체
require함수
exports 객체
: 여러 개의 속성과 메소드를 정의.
1. calculator.js파일을 생성: add, sub
/* calculator.js */
const initialNumber = 0
// calculator.js내에서만 유효한 private변수
exports.add = (x, y) => initialNumber + x + y;
// export 객체에 add method 정의: 외부 공개!
exports.substract = (x, y) => initialNumber + x - y;
// export 객체에 substract method 정의: 외부 공개!
require()
함수를 사용해 calculator모듈을 import하기/* main.js */
const calculator = require("./calculator.js");
// calculator모듈은 객체 형태로 반환되어 상수calculator에 담김
console.log(`1 + 5 = ${calculator.add(1, 5)}`);
// calculator.add 라는 형식으로 공개된 calculator.js파일을 참조
console.log(`1 - 5 = ${calculator.subtract(1, 5)}`);
// calculator.subtract 라는 형식으로 공개된 calculator.js파일을 참조
node main
module.exports
: 하나의 객체만 할당.
/* calculator.js */
const initialNumber = 0
function add(a, b) {
return a + b;
}
function substract(a, b) {
return a - b;
}
module.exports = { // 하나의 객체만을 할당
add,
substract
}
/* main.js */
const calculator = require("./calculator.js");
console.log(`1 + 5 = ${calculator.add(1, 5)}`);
console.log(`1 - 5 = ${calculator.substract(1, 5)}`);
/* main.js */
const {add, substract} = require("./calculator.js");
// 구조분해 할당을 활용하면;
console.log(`1 + 5 = ${add(1, 5)}`);
console.log(`1 - 5 = ${substract(1, 5)}`);
exports
는module.exports
를 참조하고 있기 때문에exports
는module.exports
와 같다고 보아도 무방합니다. 공식문서에서도exports
는module.exports
의 축약형(shortcut)이라고 언급되어 있습니다. 그래도 혼란스럽다면,module.exports
를 사용합니다.
/* index.js */
const hello = 'Hello world';
console.log(hello)
node index.js
파일 시스템(File system)에서 파일을 읽고 쓰는 기능=> Node.js에서 기본으로 제공하는 파일 시스템 모듈(module)을 사용한다!
예제1)
require() 함수를 사용해서 fs모듈을 호출 ==> require('fs')
모듈 안에서 반환된 객체를 fileSystem 변수에 담고 ==> const fileSystem =
const fileSystem = require('fs')
예제2) fileSystem 객체를 사용한 데이터 읽고 쓰기
/* privateInfomation.txt */
Name : Harry Potter
Age : 22 // 22 다음에 엔터를 칠 것!
/* syncFileSystem.js */
const fileSystem = require("fs");
//Read File (Before)
const privateInformation = fileSystem.readFileSync("./privateInformation.txt", "utf-8");
console.log(`===== Before written ===== \n${privateInformation}`);
//Write File
const additionalInfo = `${privateInformation}PhoneNumber : 010-7777-0000\nModified at : ${Date.now()}`;
fileSystem.writeFileSync("./privateInformation.txt", additionalInfo);
console.log("===== Successfully File written! ===== \n");
//Read File (After)
const updatedPrivateInformation = fileSystem.readFileSync("./privateInformation.txt", "utf-8");
console.log(`===== After written ===== \n${updatedPrivateInformation}`);
//추가적으로 실행되어야 하는 코드 (동기적 실행을 이해하기 위해서 추가된 코드 입니다.) (1)
console.log("추가적으로 실행되어야 하는 코드 1");
console.log("추가적으로 실행되어야 하는 코드 2");
console.log("추가적으로 실행되어야 하는 코드 3");
console.log("추가적으로 실행되어야 하는 코드 4");
에서
fileSystem.readFileSync
메서드를 사용해 파일에 접근합니다
첫번째 인자"./privateInformation.txt"
: 파일이 위치한 경로
두번째 인자"utf-8"
: 문자의 인코딩을 정의
fileSystem.readFileSync("./privateInformation.txt", "utf-8");
Sync
: synchronic(동기). 코드가 한줄 한줄씩 처리됨node syncFileSystem.js
결과: 실행순서와 동일하게 출력됨
텍스트 파일 수정 후:
동기적 실행으로 파일 시스템 접근 & 읽어오는 방식은 좋지 않습니다.
👉 파일을 읽은 후에 추가적으로 실행되야 하는 코드들이 있다면, 파일을 읽은 작업 동안 실행이지연
되기 때문입니다.(블로킹 문제)
👉 비동기적 실행의 필요!
예제1) fileSystem.readFile
, fileSystem.writeFile
메서드로 비동기적 실행해 파일 읽기,쓰기
// CASE 2. Non-blocking, Asynchronous
// asyncFileSystem.js
const fileSystem = require("fs");
// Read File (Before)
fileSystem.readFile("./privateInformation.txt", "utf-8", (err, data1) => {
console.log(`===== Before written ===== \n${data1}`);
const additionalInfo = `${data1}PhoneNumber : 010-7777-0000\nModified at : ${Date.now()}`;
// Write File
fileSystem.writeFile("./privateInformation.txt", additionalInfo, "utf-8", (err, data2) => {
console.log("===== Your file has been written =====");
// Read File (After)
fileSystem.readFile("./privateInformation.txt", "utf-8", (err, data3) => {
console.log(`===== After written ===== \n${data3}`);
});
}
);
});
//추가적으로 실행되어야 하는 코드
console.log("\n===== 추가적으로 실행되어야 하는 코드 =====");
console.log("추가적으로 실행되어야 하는 코드 1");
console.log("추가적으로 실행되어야 하는 코드 2");
console.log("추가적으로 실행되어야 하는 코드 3");
console.log("추가적으로 실행되어야 하는 코드 4");
에서 readFile()
메서드로 파일시스템에 접근합니다
"./privateInformation.txt"
: 파일이 저장된 경로"utf-8"
: 인코딩(err, data3) => { console.log(``===== After written ===== \n${data3}``);}
: Callback 함수fileSystem.readFile("./privateInformation.txt", "utf-8", (err, data3) => {
console.log(`===== After written ===== \n${data3}`);
});
실행 결과: 마지막 코드가 먼저 실행되고, 파일 읽기/쓰기 작업은 나중에 출력됨
깊게 이해하기(Node.js 동작 원리 편)
동기, 비동기, blocking, non-blocking, Single-thread, Thread Pool, event-loop
: Node.js 기반의 패키지를 사용하기 위한 패키지 관리 도구.
npm으로 어떻게 서드 파티 패키지를 관리하고 사용하는지 알아보자:
: 프로젝트에 대한 메타 정보와 프로젝트에서 사용중인 npm 패키지에 대한 정보를 담은 파일.
버전
까지 명확하게 기록해야 한다. 동일버전을 설치해야한다# project 폴더에서 아래 명령어 실행
$ npm init
Press ^C at any time to quit.
package name: (nodejs-study) westarbucks
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: lordmyshepherd
license: (ISC) MIT
About to write to /Users/lims/software_engineer/nodejs-study/package.json:
# npm init이 완료되면 폴더에 package.json이 생성됨
# pacakage.json 파일
{
"name": "api", // 패키지의 이름
"version": "1.0.0", // 패키지의 버전
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "lordmyshepherd",
"license": "MIT",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"jsonwebtoken": "^8.5.1",
"mysql2": "^2.3.3",
"typeorm": "^0.3.0"
},
"devDependencies": {
"nodemon": "^2.0.15"
}
}
name
: 패키지 이름. package.json의 name 속성에 저장version
: 패키지 버전. 다소 엄격하게 관리.main
: package 엔트리 포인트.scripts
: node.js command line을 정의해서 사용git repository
: 소스코드를 저장해둔 Git 저장소 주소.keywords
: 키워드는 npm 공식 홈페이지에서 패키지 탐색 용이성. package.json의 keywords 속성에 저장됨.license
: 해당 패키지의 라이선스를 넣음.터미널 명령어(위 속성명 참조):
로컬 개발용 패키지 | 배포용 패키지 | |
---|---|---|
설치 명령어 | node install --save-dev (패키지명) | node install (패키지명) |
추가되는 속성 | devDependencies | dependencies |
실행하면 다음과 같은 속성이 추가됨(위 참조):
dependencies
: 배포용 패키지와 각 버전을 명시.devDependencies
: 로컬 개발용 패키지와 각 버전을 명시.: npm install 명령어를 실행할 때 생성되는 node_modules 디렉토리.
: node_modules 디렉토리 내부에 설치한 패키지들(express, cookie, 등)이 포함됨
: 프로젝트에 필요한 패키지들을 디렉토리에서 전부 관리할 수 있어 편리함
예) http server 개발을 빠르게 하기 위해서 express(👉api개발의 핵심!)라는 라이브러리를 설치했을 때, express 외에도 express와 의존 관계가 있는 패키지들이 모두 설치되어 있는 것을 확인할 수 있다.
.
├── body-parser
│ └── lib
│ └── types
├── bytes
├── content-disposition
├── content-type
├── cookie
├── cookie-signature
├── express
│ └── lib
│ ├── middleware
│ └── router
├── finalhandler
├── forwarded
├── fresh
├── http-errors
...
package-lock.json도 생성되어 패키지 간 의존 관계를 명확하게 표시함.
npm outdated
: 사용하는 어떤 패키지에 업데이트된 내역을 확인.
Package Current Wanted Latest Location Depended by
body-parser 1.19.2 1.20.0 1.20.0 node_modules/body-parser NodeJS-simple-api-server-with-express
express 4.17.3 4.18.1 4.18.1 node_modules/express NodeJS-simple-api-server-with-express
npm install + package
: 배포용 패키지 설치 후 package.json의 dependencies 속성에 추가.
npm install --save-dev + package
: 개발용 패키지 설치 후 package.json의 devDependencies 속성에 추가.
npm uninstall + package
: 패키지를 삭제
(npm rm + package
도 가능)
npm search + 검색어
: npm 패키지를 검색.
npm info + package
: 패키지의 세부 정보 출력.
npm login
: npm에 로그인을 하기 위한 명령어로 사전에 https://npmjs.com 에서 회원가입이 필요.
npm whoami
: 현재 사용자가 누구인지 출력.
npm logout
: 로그인한 계정 로그아웃.
npm version + major
: package.json에서 major version 올림.
npm deprecate + [package][version][message]
: 패키지를 설치할 때 경고 메시지를 띄우게 함.
npm publish
: 만든 패키지를 npm에 배포.
npm unpublish
: 만든 패키지의 배포 중단.