[Node.js] Node.js 개념 정리

SeHoony·2022년 8월 20일
1

면접 대비

목록 보기
3/4

1. 공부하는 이유

개발 공부를 하면서 프론트, 백, 데브옵스처럼 커리어를 한정짓고 싶지 않았고 웹개발자라면 웹에서 발생하는 여러 문제들을 해결할 수 있는 역량이 필요하다고 생각했다. 그런 역량을 위해서는 웹 전반에 대해 이해할 수 있어야 한다.

하지만 생각처럼 백엔드 쪽 공부를 할 수 있는 상황이 잘 주어지지 않았다. 그래서 정글이 끝나면 가장 먼저 할 것으로 백엔드 공부를 생각했다. 그리고 이제서야 차근차근 배워나가게 되었다.

아시다시피 Node.js는 자바스크립트를 브라우저에서 뿐만 아니라 그 밖에서도 쓸 수 있게 한다는 점이 장점이다. 그래서 백엔드 뿐만 아니라 여러 Automation 라이브러리를 구현할 수도 있어, 이번 기회에 여러가지 시도를 꿈꿔본다.

자아, 공부하러 가보자.

2. Node.js 작동원리

Node.js는

  • JavaScript 런타임 환경
  • Single Thread
  • Non-Blocking I/O
  • Event Driven

주요 4가지 특징을 가지고 있다. 이러한 특징을 통해 Sigle Thread이지만 비동기의 특징으로 I/O작업을 효율적으로 할 수 있게 된다.
자세한 내용은 전에 정리했던 Node.js 작동원리 포스팅을 확인해보자.

3. Node Modules

CRA를 하면 항상 자동으로 생성되는 거라서 별 생각이 없었다. 하지만 Node.js가 제공하는 여러 라이브러리를 담고 있기 때문에 매우 중요하다는 것을 한 번 더 생각할 수 있었다.

3-1. Global

  • (Browser) Window 객체 === global
  • (Node.js) global 객체 === global

3-2. this

  • (함수내) global
  • (Class 내) class 자기 자신
  • (밖) 아무것도 아님

3-3. module(export, import)

이 부분이 정글 마지막 프로젝트에서 나를 힘들게 했던 것이라 정확히 알고 싶었다.

라이브러리를 설치할 때, 구글링한 정보마다 module import하는 방법이 달라서 계속 오류가 나서 힘들었다.

원래 방법

  • export

    [counter.js]
    
    function getCount() {
      return count;
    }
    module.exports.getCount = getCount;
  • import

    [app.js]
    
     const counter = require("./counter");

개정 방법

  • export

    [counter.js]
    
    export function getCount() {
        return count;
    }
    
  • package.json에 'type : module' 추가

    {
     "name": "5-module",
     "version": "1.0.0",
     "description": "",
     "main": "app.js",
     "type" : "module",
     "scripts": {
       "test": "echo \"Error: no test specified\" && exit 1"
     },
     "author": "",
     "license": "ISC"
    }
    
  • import

    [app.js]
    
     import { getCount} from './counter.js'

3-4. OS / Process

node.js에서 제공하는 모듈을 통해 os, process에도 접근할 수 있다.

const os = require('os');
const process = require('require');

3-5. Timer

Timer 관련 모듈을 통해서 Call StackTask Queue를 넘나들며 활용할 수 있다.

const process = require("process");

console.log(1);
console.log(2);

setTimeout(()=>{
console.log("setTimeOut")
},0)

process.nextTick(() => {
  console.log("nextTick");
});

for (let i = 0; i < 10; i++) {
    console.log('loop : ', i)
}

위의 식이 있을 때, 어떻게 발동할 지 예상해보자.

  1. 스크립트의 위에서부터 Call Stack에 함수를 넣어 발동한다.
  2. 이 때 SetTimeout node module을 만나 0초 후에 TaskQueue에 콜백함수를 저장한다.
  3. process.nextTick(callback())를 통해 Task Queue에 콜백함수를 저장하는데, 이 때 우선순위는 가장 먼저다.
  4. 그리고 다시 for문을 Call Stack에 넣어 모든 과정을 거친다.
  5. for문이 모두 실행되면 Call Stack이 비게 되는데, 이 때 Task Queue에 있던 함수들이 발동된다.
  6. Task Queue에 쌓여있는 순서대로 Call Stack으로 옮겨가 발동된다.

결과


즉, setTimeout(()=>{},0)이라고 하더라도 0초 후에 발동하는 것이 아니다. Call Stack에 얼마나 크기 큰 함수가 있느냐에 따라 발동시간의 차이가 있다.

3-6. Path

Node.js는 fileSystem에 접근하기 유리하다. 이렇게 fileSystem에 접근할 때 그 경로를 찾기 위해 path를 쓴다.

[app.js]
const path = require('path')
  • __dirname : 본 파일이 포함된 디렉토리명
  • __filename : 본 파일의 이름

  • path.sep : 경로의 구분자
  • path.delimiter : 환경변수 구분자

  • path.basename(__filename) => app.js
  • path.basename(__filename, '.js') => app
  • path.dirname(__filename)
  • path.extname(__filename) => .js
  • path.parse(__filename) =>
{
  root: 'C:\\',  
  dir: 'C:\\Users\\Sehoon\\projects\\back_end_practice\\node\\9-path',
  base: 'app.js',
  ext: '.js',
  name: 'app'
}

  • path.join(__dirname, 'image') => 각 OS 환경에 맞게 구분자를 정해서 알아서 경로를 붙여준다.

3-7. file

file 관련 node module에 대해 알아보자

  • rename : (비동기) 파일명 변경

    const fs = require('fs')
    [방법 1]
    fs.rename('./a.txt', './b.txt', (err) => {
        console.log(err)
    })
    
     [방법 2]
    fs.promises
      .rename("./text.txt", "/text-new.txt")
      .then(() => {
        console.log("DONE!");
      })
      .catch(console.error);
    
  • 읽고, 쓰고, 복사하고, 디렉토리 만들고

    const fs = require('fs').promises
    
     fs.readFile('./text.txt', 'utf-8').then(
     (data) => {console.log(data)})
     .catch(console.error)
    
     fs.writeFile("./file.txt", "Hello, Sehoon").catch();
    
    fs.appendFile("./file.txt", "Yo, Sehoon")
    .then(()=>{
       fs.copyFile("./file.txt", './file2.txt')
    }).catch();
    
    fs.mkdir('sub-folder').catch(console.error)
    
    fs.readdir('./').then(console.log).catch(console.error) 
     => 안에 파일들이 배열로 나온다.

    4. Buffer & Stream

    4-1. Buffer

    전체 메모리를 고정 크기로 토막내서 저장한 것으로 bye의 배열이다.

    Buffer.from(string)
    Buffer.alloc(number)
    buf.toString()

  • buffer 생성

    const buf = Buffer.from('Hi')

    buffer 내에 유니코드가 배열 형식으로 나오게 된다.
    buf[0], buf[1]로도 접근 가능한데
    이때는 ASCII 코드로 나온다.

  • buffer 다시 string으로
    buf.toString()

  • buffer 공간 생성

	const buf = Buffer.alloc(2)
	buf[0] = 72
	buf[1] = 105
	console.log(buf.toString()) => 글자 나옴
  • 글자 합치기
	Buffer.concat([buf1, buf2, buf3])

4-2. Stream

1. READ

fs.createReadStream('읽을 파일 경로')

fs.createReadStream('./file.txt',)
.on('data' , chunk => {
	data.push(chunk)
})
.on('error',console.error)
.on('end', () => {console.log(data.join(''))})

2. WRITE

fs.createWriteStrea('새로 쓸 파일 경로')

const writeStream = fs.createWriteStream('./file.txt',)
.on('finish' , () => {console.log('finish')})

writeStream.write('HELLO')
wrtieStream.wrtie('SEhoon')
writeStream.end()

3. PIPE

const zlib = require("zlib");
const zlibStream = zlib.createGzip();

const piping = readStream.pipe(zlibStream).pipe(writeStream);
piping.on("finish", () => {
  console.log("done!");
});
profile
두 발로 매일 정진하는 두발자, 강세훈입니다. 저는 '두 발'이라는 이 단어를 참 좋아합니다. 이 말이 주는 건강, 정직 그리고 성실의 느낌이 제가 주는 분위기가 되었으면 좋겠습니다.

0개의 댓글