Node.js : 특징, 이벤트 루프, 구성, 모듈 시스템, API

김종현·2023년 3월 27일
0

Node.js

목록 보기
2/8

Node.js

-크로스 플랫폼 실행, 제한 없는 동작, 다양한 어플리케이션 개발.
-고성능 Js 발달과 V8 엔진의 등작으로 탄생.

vs. Js
-브라우저에서 실행, 웹 내부 제한된 동작, 웹 프론트 개발자의 언어

특징

1)싱글 쓰레드

  • 한 번에 한가지 동작만 수행.
  • 쓰레드가 늘지 않아 리소스 관리에 효율적이나 쓰레드 기반 작업들의 효율 떨어짐.

2)비동기

  • 비동기 동작으로 쓰레드 기반의 작업을 최소화 함.

3)이벤트 기반
-비동기 동작의 완료를 처리하는 방법으로 특정 동작 실행 후 신경 쓰지 않음.
-대신 해당 동작이 완료될 경우 실행할 함수를 미리 등록, 동작 완료시 함수 실행.

요약 : 싱글 쓰레드이므로 비동기 동작 필요하고 이를 구현하기 위해 이벤트 기반으로 돌아감.

브라우저와 Node.js의 이벤트 루프

-기본적으로 동작방식에 둘 사이 큰 차이 없음.

이벤트 루프 구성요소

1)콜스택 작동 순서

2)메시지큐 작동 순서

3)잡큐 작동 순서

-이벤트 루프는 비동기 동작의 실행 타이밍을 이해해야 한다.
-setTimeout은 콜스택이 비어있을 때 실행 된다.
-Promise는 상위함수가 종료되기 전에 실행 됨.

어플리케이션에 대해서

  1. 어플리케이션 유형
    -플랫폼에 따라 다름 : 브라우저, 모바일 OS, 데스크탑 OS< IoT기기용 OS
    -각 플렛폼에서 허용하는 언어를 사용해서 실행가능한 어플리케이션 개발
    -혹은 중간다리 역할을 하는 실행환경(Runtime, JRE, Node.js)등을 두어서 원하는 언어로 어플리케이션 개발
    -어플리케이션은 항상 시각적으로 보이진 않음. 보이지 않는 어플리케이션도 많음
    -Node.js의 경우 보이지 않는 어플리케이션 개발을 할 때 사용

  2. GUI와 TUI(CLI) : 컴퓨터와의 소통창 -1
    -GUI(Graphical User Interface) : 그래픽/키보드/마우스/기타 IO 디바이스로 OS 또는 앱을 조작할 수 있도록 해주는 인터페이스. 일반인을 위한 인터페이스
    -TUI/CLI(Text User INterface, Command-line Interface) : 오직 키보드(간혹 마우스)로 명령어를 타이핑해서 OS 또는 앱을 조작할 수 있도록 해주는 인터페이스. 개발자들이 사용해야하는 인터페이스.
    -웹/모바일 FE는 GUI용 앱을 개발 : 시각적 프로그램 제작
    -웹 BE는 TUI/CLI용 앱을 개발 : 유저에게 보이지 않아도 되는 로직을 수행하는 프로그램 제작(BE는 CLI 환경에 익숙해야한다)

Node.js란?

  1. Node.js 이전의 Js
    -브라우저의 전유물, 웹페이지 작성에만 사용, 웹페이지 interactive하게 만들어주는 언어.

  2. 서버 사이드 Js
    -Js를 브라우저가 아닌 OS에서 실행시키고자 하는 시도
    -Isomorphic Js를 향한 목표.

  3. Chromium의 V8 엔진 출현
    -Js 엔진 중 오픈소스로 개발된 엔진
    -OS에서 앱에 제공하는 다양한 기능(파일 시스템 접근, 네트워크 접근, 시스템 접근)들을 사용해서 브라우저에서는 불가능한 것들을 할 수 있게 됨.

  4. Ryan Dahl의 Node.js
    -V8엔진과 libuv(event loop), Node binding(c++라이브러리 집합)로 이루어진 자바스크립트 실행환경(Runtime)
    -비동기 실행 방식을 기본적으로 지원하여 IO(input/output)작업 시 최고의 성능을 보여줌
    -적절한 시점과 적절한 성능 그리고 NPM(Node.JS Package Manager)으로 구성된 생태계로 인기 얻음
    -서버 모듈을 가져 자체적으로 서버 역할을 할 수 있어 간단한 서버 구축에 좋다

Node.js 구성

  1. V8 엔진
    -Js 코드 해석 후 작업 실행, 코드를 실행하는 것만 담당

  2. libuv(event loop)
    -c로 작성된 이벤트 루프(브라우저에 탑재된 것과는 다름)
    -Node.js API 또는 C/C+ addon으로 실행된 비동기 작업이 마친 후 실행되어야 하는 콜백함수들을 각종 큐에서 정해진 순서에 맞게 꺼내와서 자바스크립트 엔진에게 전달해줌.
    -싱글 스레드로 작동
    -Node.js를 위해 개발되었으나 현재는 다른 언어들도 사용

  3. Node.js API : Js/C/C++ 기반 라이브러리
    -Node.js에서 제공하는 독자적인 API들
    -브라우저가 아닌 일반 OS 환경에서 실행되기 때문에 브라우저의 Web API와는 다른 라이브러리들이 포함되어있음.
    -Js에서 사용 가능한 API와 Node.js에서 사용 가능한 API들 구분이 필요함
    -fs, path, crypto, Stream, zlib, child㏄process, EventEmitter

(1)Node.js API - fs
-비동기와 동기 메소드가 모두 구비되어있다.
-Promise를 반환하는 fs.promises 모듈이 별도로 있다.
-특정 파일을 읽어서 가공한 후 다른 파일에 쓰는 작업을 단계 별로가 아닌 모두 연결해서 공장 파이프라인처럼 처리할 수 있다.
이러한 것을 스트림 용어로 파이프라이닝(다른 곳에서도 많이 사용되는 용어)이라고 한다

// fs와 pipeline은 모두 promise 버전도 있으니 참고할 것!
const fs = require("fs");
const pipeline = require("stream").pipeline;

// 읽어오는 파일의 stream
const readStream = fs.createReadStream("./cities.txt", { encoding: "utf8" });
readStream.on("close", () => {
  console.log("read stream이 닫혔습니다.");
  //스트림 읽기, 다 읽고 나면 콘솔찍기
});
// 쓰는 대상 파일의 stream
const writeStream = fs.createWriteStream("./new_cities.txt", {
  encoding: "utf8",
});
writeStream.on("close", () => {
  console.log("write stream이 닫혔습니다.");
  //스트림 사용, 다 쓰고 나면 콘솔찍기
});

async function main() {
  // 두 stream을 연결해서(pipeline) 읽고 쓰기를 한 꺼번에 진행한다.
  pipeline(readStream, writeStream, (error) => {
    if (error) {
      console.log(error);
      return;
    }
    console.log("데이터 이전을 완료!");
  });
}

main();

//작업이 완료되면 cities.txt를 복사한 new_cities.txt가 생성.

(2)Node.js API - path
-복잡할 수 있는 상대/절대 경로를 용이하게 생성할 수 있도록 도와준다

const path = require("path");

console.log("현재 파일 경로:", __dirname);
console.log("현재 파일 이름:", __filename);
console.log("현재 운영체제 경로 구분자:", path.sep);
// Mak , Linux => /
// window      => \
console.log("현재 파일에 대한 정보:", path.parse(__filename));
//현재 파일 정보는 잘 안씀

console.log("현재 파일 이름+확장자:", path.basename(__filename));
console.log("현재 파일 이름만:", path.basename(__filename, ".js"));
console.log("현재 파일의 확장자:", path.extname(__filename));
console.log("현재 파일이 있는 디렉토리(폴더)", path.dirname(__filename));

console.log();
// 절대경로인지 체크
console.log(
  `현재 경로${__dirname}가 절대 경로인가?`,
  path.isAbsolute(__dirname)
);
console.log(
  `현재 경로${".." + __dirname}가 절대 경로인가?`,
  path.isAbsolute(".." + __dirname)
  //..을 붙여 상대경로가 됐으니 false가 뜸
);

const someImage = "some_image.jpg";
console.log("윈도우에서만 유효한 경로:", __dirname + "\\" + someImage); // 안좋은 방식
console.log("모든 OS에서 유효한 경로1:", __dirname + path.sep + someImage);
console.log("모든 OS에서 유효한 경로2:", path.join(__dirname, someImage)); // 더 편한 방식


  1. Module System

모듈 시스템(Module System)

-각 소스 코드를 독립적인 개체(모듈)로 묶어서 관리하는 방식
-각 소스 코드 간의 scope 간섭을 없애고 각자의 소스 코드가 독립적인 scope을 갖도록 해줌
-ex) a.js와 b.js 사이 변수를 막 가져다 쓰지 못하게 함.
-용도/책임/역할 등에 따라서 코드를 분리해서 관리할 수 있음

  1. 두 가지 모듈 방식
    -ECMAScript 표준 모듈 시스템 : 비동기
    -Node.js의 CommonJs : 동기

(1) CommonJs
1)각 디렉토리의 index.js는 디렉토리 이름을 대표한다
-예를 들어 /src/sample/index.js 경로에 있는 파일에서 module.exports = { a: 1 }을 해놓으면 다른 파일(/src/app.js)에서
require("./sample");만으로 index.js에 있는 모듈을 사용할 수 있다

// ../sample/a.js
function sayA() {
  console.log("This is A");
}

module.exports = {
  sayA,
};

// ../sample/b.js
function sayB() {
  console.log("This is B");
}

module.exports = {
  sayB,
};

// ../sample/index.js
const a = require("./a");
const b = require("./b");

/**
 * a 모듈과 b모듈을 index.js에서 export해줌으로서
 * 해당 모듈들을 require하는 파일이 편하게 require할 수 있도록 할 수 있다.
 */
module.exports = {
  a,
  b,
};

// 위는 아래와 같다
// const a = require("./a");
// const b = require("./b");

// module.exports = {
//   a: a,
//   b: b,
// };

// sample 상위 디렉토리 index.js
const { a, b } = require("./sample");

a.sayA();
b.sayB();

// 위는 아래와 같다
// const sample = require("./sample");

// sample.a.sayA();
// sample.b.sayB();

const fs = require("fs");
const test = require("./test.json");
//require는 근처 파일 가져오기, fetch는 원격으로 가져오기. 
//req가 더 제한적임. node.js는 axios 쓰기. fetch는 기능이 전부 작동x
console.log("require로 읽어온 json 파일");
console.log(test);

// fs로 파일을 읽으면 일반 string으로 읽어온다.
const testString = fs.readFileSync("./test.json", "utf8");

console.log("fs로 읽어온 json파일");
console.log(testString);
// JSON.parse로 해주어야 JS의 객체로 다룰 수 있다.
console.log(JSON.parse(testString));

2)index.js를 잘 활용하면 간결하게 require문을 작성할 수 있다
-예를 들어 /src/sample/index.js에서 sibling 파일들을 다 require하고 다시 module.exports로 export를 해주면 다른 파일 (/src/app.js) 에서 한 줄로 sample 디렉토리 아래에 있는 모듈들을 가져올 수 있다

3)json파일을 require해서 일반 객체처럼 사용이 가능하다. fs로 파일을 읽어서 JSON.parse를 쓰는 것보다 편하다

  1. ESM vs CJS
    -Node.js 생태계의 많은 모듈들이 CJS로 개발되어 있다
    -CJS를 지원하다 포기한 모듈들도 나타나기 시작했다. ex)Got
    -따라서 둘 다 다룰 줄 알아야한다.
  1. 모듈 시스템의 유무
    (1) 모듈 시스템 부재

(2) 모듈 시스템 존재

(3) 모듈의 재사용 : package

Node.js API

const fs = require("fs");
const fsp = require("fs").promises;
const path = require("path");

const filename = "cities.txt";
const filePath = path.join(__dirname, filename); // 절대 경로
//__dirname : 예약어
// home/user/document/elice_sw/0328/JS/cities.txt 슬래쉬로 시작하면 절대 경로.
// ./../ <~ 상대경로

const newFilename = "web_101.txt";
const newFilePath = path.join(__dirname, newFilename);
//파일을 만들기 위한 코드
//home/user/document/elice_sw/0328/JS/web_101.txt 이런식으로 나옴.

function callCallbackStyles() {
  // 콜백 방식으로 파일을 읽어오기
  //readFile, writeFile은 비동기로 실행됨.
  fs.readFile(filePath, "utf8", (error, data) => {
    if (error) {
      console.log(error);
      return;
    }
    console.log(data);
  });

  // 파일 생성(overwrite), 콜백 방식
  fs.writeFile(newFilePath, "javascript\n", "utf8", (error) => {
    if (error) {
      console.log(error);
      return;
    }
    console.log("파일 생성 성공!");
  });
}

async function callPromiseStyles() {
  // 프로미스 방식으로 파일 읽어오기
  await fsp
    .readFile(filePath, "utf-8")
    .then((data) => {
      console.log(data);
    })
    .catch((error) => {
      console.log(error);
    });

  // 파일 생성(overwrite), 프로미스 방식
  await fsp
    .writeFile(newFilePath, "html\n", "utf8")
    .then(() => console.log("파일 생성 성공!!"))
    .catch((error) => console.log(error));

  // 파일 이어쓰기
  await fsp
    .appendFile(newFilePath, "javascript\ncss", "utf8")
    .then(() => console.log("파일 이어쓰기 성공!!"))
    .catch((error) => console.log(error));
}

async function main() {
  // callCallbackStyles();
  callPromiseStyles();
}

main();
profile
나는 나의 섬이다.

0개의 댓글