BE_intro_2. Node.js의 기능_10.25

송철진·2022년 10월 25일
0

0.학습 목표

  • Node.js 란, 무엇을 위해서 만들어진 기술인지
  • Javascript, V8 엔진, Server-side, Runtime
  • Node.js로 개발하면서 얻을 수 있는 장점과 사용하는 이유
  • 운영체제별 Package manager를 사용해서 Node.js를 설치 방법
  • REPL (Read Eval Print Loop)과 .js 파일을 사용해서 javascript 코드를 node.js(실행환경)에서 실행
  • Node.js의 모듈 시스템, Custom 모듈을 직접 만들기
  • Node.js에 내장되어 있는 모듈 중에 가장 중요한 file system 모듈을 사용, 동기/비동기에 대한 개념 이해하기

요약

  • 모듈화가 잘 된 코드는 재사용성과 확장성을 높여 새로운 기능을 개발하거나 유지보수 할 때, 전체적인 비용을 감소시키고 개발팀의 생산성을 증대시킬 수 있습니다.
  • 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 기반의 다양한 패키지를 설치하고 버전을 관리할 수 있게 도와주는 패키지 관리 도구 입니다.

1. Node.js module system

1-1. Module과 Node.js module system

모듈 = LEGO 블록

LEGO 블록: 독립적 존재, 사용자의 조립방법과 블록 종류에 따라 여러 결과물될 수 있음
모듈: 코드의 조각, 여러 모듈을 모아 하나의 소프트웨어가 됨

  • 코드의 모듈화가 중요한 이유? 유지보수가 쉬운 구조 만들기 위해!=비용 감소, 생산성 증대

but 초기 JS는 모듈화X => ES6 이후 모듈화!

CommonJS

: JS를 브라우저, 서버side애플리케이션, 데스크톱 애플리케이션에서도 사용할 수 있게 명세(Specification)를 만든 그룹.
Node.js는 이런 모듈화 작업의 결과물

CommonJS의 모듈 명세

: 모듈을 어떻게 정의하고, 어떻게 사용할 것 인지.
1. 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 함
= 전역변수(Global variable)와 지역변수(Local variable)의 분리.
예) 서버 사이드 JavaScript: 파일마다 독립적인 파일 스코프가 있어, 파일 하나에 모듈 하나를 작성하는 방법으로 독립적인 실행 영역 구현.
2. 모듈을 외부에서 사용할 수 있도록 공개.
Node.js (CommonJS의 모듈 명세를 따르는)에서는 exports라는 전역 객체를 이용해서 정의.
3. 모듈을 사용하는 영역에서는 require() 함수를 이용해서 모듈을 불러옴.

1-2. Custom module 만들기

모듈은 파일과 1대1 대응 관계, 하나의 모듈은 자신만의 독립적인 실행영역(스코프)를 가짐.

  • 정의하고 외부로 공개: module.exports, exports 객체
  • 공개된 모듈을 임포트: require함수

1-2-1. exports객체

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 정의: 외부 공개!
  1. main.js에서 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파일을 참조
  1. main.js코드 실행하기
node main

1-2-2. module.exports

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)}`);

exportsmodule.exports를 참조하고 있기 때문에 exportsmodule.exports와 같다고 보아도 무방합니다. 공식문서에서도 exportsmodule.exports의 축약형(shortcut)이라고 언급되어 있습니다. 그래도 혼란스럽다면, module.exports를 사용합니다.

2. Built-in modules - File system module

2-1. Node.js로 js file 실행

  1. index.js 파일 생성
/* index.js */
const hello = 'Hello world';
console.log(hello)
  1. 터미널에서 node로 index.js파일 실행
node index.js

2-2. 파일RW: Synchronously

파일 시스템(File system)에서 파일을 읽고 쓰는 기능=> Node.js에서 기본으로 제공하는 파일 시스템 모듈(module)을 사용한다!

예제1)
require() 함수를 사용해서 fs모듈을 호출 ==> require('fs')
모듈 안에서 반환된 객체를 fileSystem 변수에 담고 ==> const fileSystem =

  • 파일 시스템에 데이터를 읽고 쓰는 기능이 필요할 때마다 사용한다.
    (fs 모듈의 기능 공식문서)
const fileSystem = require('fs')

예제2) fileSystem 객체를 사용한 데이터 읽고 쓰기

  • 대상: privateInfomation.txt (이름, 나이)
  • 수행: node.js 환경에서 privateInfomation.txt 안에 '핸드폰 번호, 수정일'을 추가 & 저장
/* 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

결과: 실행순서와 동일하게 출력됨

텍스트 파일 수정 후:

동기적 실행으로 파일 시스템 접근 & 읽어오는 방식은 좋지 않습니다.
👉 파일을 읽은 후에 추가적으로 실행되야 하는 코드들이 있다면, 파일을 읽은 작업 동안 실행이 지연되기 때문입니다.(블로킹 문제)
👉 비동기적 실행의 필요!

2-3. 파일RW: Asynchronously

예제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}`);
});

실행 결과: 마지막 코드가 먼저 실행되고, 파일 읽기/쓰기 작업은 나중에 출력됨

  • 백그라운드에 비동기 작업을 위임
  • File I/O 작업이 오래 걸리더라도 추가적으로 실행되어야 하는 코드들에 영향을 주지 않는다

깊게 이해하기(Node.js 동작 원리 편)
동기, 비동기, blocking, non-blocking, Single-thread, Thread Pool, event-loop

3. NPM (Node Package Manager)

3-1. NPM이란?

npm(node package manager)

: Node.js 기반의 패키지를 사용하기 위한 패키지 관리 도구.

  • 패키지를 설치하고 버전을 관리.
  • Node.js를 설치하면 npm이 자동 설치됨
  • NPM = 3rd-party module의 소스 코드들이 모아져 있는 저장소(npm registry) + 웹 사이트(website) + npm CLI

모듈의 종류

  1. 커스텀(Custom) 모듈: Node.js CommonJS 방식으로 만들 모듈
  2. 빌트인(Built-in) 모듈: Node.js가 설치되면서 기본으로 내장된 모듈
    ex) require 함수
  3. 3rd-party module: 다른 개발자들이 만들어 놓은 모듈

npm 왜 사용할까?

  • 생산성을 높여주고 효율적으로 개발하기 위해.

npm으로 어떻게 서드 파티 패키지를 관리하고 사용하는지 알아보자:

3-2. package.json

정의 및 특징

: 프로젝트에 대한 메타 정보와 프로젝트에서 사용중인 npm 패키지에 대한 정보를 담은 파일.

  • Semantic versioning규칙으로 패키지 버전을 명세
  • 팀원과 공유하여 모든 팀원이 동일한 환경에서 개발 가능
  • 프로젝트 초기 세팅 시 package.json부터 만들고 시작한다.
  • 프로젝트 별 요구되는 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 파일의 속성

# 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 엔트리 포인트.
    ex) 패키지화 해서 npm에 등록했을 때 다른 사람들이 이 패키지의 모듈을 가져다 쓰려면 "index.js"가 그 모듈을 import하는 엔트리포인트가 됨!
  • scripts : node.js command line을 정의해서 사용
  • git repository: 소스코드를 저장해둔 Git 저장소 주소.
    나중에 소스에 문제가 생겼을 때 사용자들이 이 저장소에 방문해 문제를 제기하고, 코드 수정본을 올림.
    package.json의 repository 속성에 저장됨.
  • keywords: 키워드는 npm 공식 홈페이지에서 패키지 탐색 용이성. package.json의 keywords 속성에 저장됨.
  • license: 해당 패키지의 라이선스를 넣음.
    ex) "MIT"는 오픈소스를 의미한다!

3rd party package 설치 방법

터미널 명령어(위 속성명 참조):

로컬 개발용 패키지배포용 패키지
설치 명령어node install --save-dev (패키지명)node install (패키지명)
추가되는 속성devDependenciesdependencies

실행하면 다음과 같은 속성이 추가됨(위 참조):

  • dependencies : 배포용 패키지와 각 버전을 명시.
  • devDependencies : 로컬 개발용 패키지와 각 버전을 명시.

3-3. node_modules

정의

: npm install 명령어를 실행할 때 생성되는 node_modules 디렉토리.

특징

: node_modules 디렉토리 내부에 설치한 패키지들(express, cookie, 등)이 포함됨
: 프로젝트에 필요한 패키지들을 디렉토리에서 전부 관리할 수 있어 편리함

예) http server 개발을 빠르게 하기 위해서 express(👉api개발의 핵심!)라는 라이브러리를 설치했을 때, express 외에도 express와 의존 관계가 있는 패키지들이 모두 설치되어 있는 것을 확인할 수 있다.

/node_modules 구조

.
├── body-parser
│ └── lib
│ └── types
├── bytes
├── content-disposition
├── content-type
├── cookie
├── cookie-signature
├── express
│ └── lib
│ ├── middleware
│ └── router

├── finalhandler
├── forwarded
├── fresh
├── http-errors
...
package-lock.json도 생성되어 패키지 간 의존 관계를 명확하게 표시함.

3-4. 자주 사용하는 npm 명령어 목록

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 : 만든 패키지의 배포 중단.

profile
검색하고 기록하며 학습하는 백엔드 개발자

0개의 댓글