Woozy_DevLog
Published 2023. 4. 28. 14:09
Node.JS란 무엇인가? Node.js

기술 면접 스터디를 하면서 Node.JS에 대해서 많은 것을 알게 되었습니다.

새롭게 배운 내용을 다시 한번 되새기며 지식으로 습득하기 위해 블로그에 다시 한번 정리해 봅니다.

 

Node.JS란 무엇인가?


Node.JS는 자바스크립트를 브라우저 밖에서도 실행할 수 있도록 하는 자바스크립트의 런타임 환경입니다.

Node.JS는 비동기 이벤트 기반 자바스크립트 런타임으로 확장성 있는 네트워크 애플리케이션을 만들 수 있도록 설계되었습니다.

 

Node.JS에 동작 원리


Node.JS는 싱글 스레드이며, 이벤트 기반 논 블로킹 모델로 구성되어 있습니다.

싱글 스레드는 프로세스 내에서 하나의 스레드가 하나의 요청만을 수행합니다.

프로세스: 메모리에 올라와 실행되고 있는 프로그램의 인스턴스를 말합니다.
스레드: 프로세스 내에서 할당받은 실행의 단위입니다.
예로 Node.JS로 구성한 서버 app을 실행하면 app 프로세스 내에서 싱글스레드를 통해 요청된 작업을 수행합니다.

Node.JS는 하나의 스레드이지만 비동기 입출력을 통하여 요청들을 서로 블로킹하지 않습니다. 그래서 동시에 많은 요청을 비동기로 수행하여 논블로킹이 가능합니다.

즉 Node.JS는 싱글 스레드이지만 비동기 처리를 통하여 논블로킹하게 작업을 수행합니다.

 

Node.JS에서 Non-Blocking I/O를 알아보자


Node.JS에서는 Input과 Output이 관련된 작업(HTTP, DataBase CRUD, Third party API, Filesystem) 등을 비동기 방식으로 처리합니다. 이러한 비동기 I/O 작업을 수행하는 함수들은 일반적으로 콜백 함수를 인자로 받아 해당 작업이 완료되면 콜백함수를 실행합니다.

아래 예시와 함께 살펴보겠습니다.

const fs = require('fs');

fs.readFile('file.txt', 'utf8', function (err, data) { // 파일을 비동기적으로 읽어옴
  if (err) {
    return console.error(err); // 에러 처리
  }
  console.log(data); // 파일 내용을 출력
});

fs.readFile() 함수를 사용하여 파일을 비동기적으로 읽어와 콜백 함수에서 이를 처리합니다.

이때 파일을 읽어오는 작업을 비동기적으로 처리하고, 작업이 완료되면 이를 처리하기 위해 콜백함수를 실행합니다.

따라서 파일을 읽어오는 작업이 수행되는 동안에도 다른 작업들을 수행할 수 있게 됩니다.

이렇게 Node.JS가 싱글 스레드임에도 불구하고, 동시성을 처리할 수 있는 이유는 Node.JS가 libuv로 구성되어 있고 libuv는 이벤트 루프 기반으로 비동기 작업들을 처리하기 때문입니다.

 

이벤트 루프


이벤트 루프는 Node.JS가 여러 비동기 작업을 관리하기 위한 구현체입니다.

이벤트 루프는 총 6개의 페이즈로 구성되어 있으며 한 페이즈에서 다음 페이즈로 넘어가는 것을 틱이라고 하며, 각 페이즈는 자신만의 큐를 관리합니다.

  • Timer Phase
    • 말 그대로 setTimeout이나 setInterval과 같은 함수가 만들어 내는 타이머들을 다룹니다.
  • Pending I/O callback phase
    • 이벤트 루프의 pendig_queue에 들어있는 콜백들을 관리합니다. 이 큐에 담기는 콜백들은 이전 이벤트 루프 반복에서 수행되지 못했던 I/O 콜백들과 에러 핸들러 콜백등이 있습니다.
  • Idle, Prepare Phase
    • 이 두 개의 페이즈는 Node.JS의 내부적인 관리를 위한 페이즈입니다.
  • Poll Phase
    • 새로운 I/O 이벤트를 다루며 watcher_queque의 콜백들을 실행합니다. watcher_queue에는 I/O에 대한 거의 모든 콜백들이 담기며 setTimeout, setInterval, close 콜백 등을 제외한 모든 콜백이 역시서 실행됩니다.
  • Check Phase
    • setImmediate의 콜백만을 위한 페이즈이며 setImmediate가 호출되면 Check Phase의 큐에 담기고 Node.JS가 Check Pase에 진입하면 차례대로 실행됩니다.
  • Close Callbacks Phase
    • socket.on('close', () => {}); 와 같은 close 이벤트 타입의 핸들러를 처리하는 페이즈입니다.

Node.JS는 순서대로 페이즈를 방문하면서 큐에 쌓인 작업을 하나씩 실행합니다. 페이즈의 큐에 담긴 작업을 모두 실행하거나 실행 한도에 다다르면 Node.JS는 다음 페이즈로 넘어가고 이벤트 루프가 살아있는 한 Node.JS는 이벤트 루프를 반복합니다.

  • Node.JS에서 비동기 작업이 요청되면 콜스택에서 libuv로 해당 작업을 보냅니다.
  • libuv는 이벤트 루프 기반으로 해당 비동기 작업을 커널 또는 스레드풀로 옮겨 작업을 진행하고 작업이 완료되면 콜백 함수를 각 페이즈에 큐에 저장합니다.
  • 그 후 Node.JS가 페이즈에 진입하고 해당 페이즈 내에 큐에서 콜 스택이 비어있다면 콜백 함수를 꺼내어 콜 스택에 넣어 실행시킵니다.
  • 각 페이즈 내 큐에 있는 작업을 다 실행하거나, 시스템의 실행 한도에 다다르면 Node.JS는 다음 페이즈로 넘어갑니다.

 

이벤트 기반


이벤트, 특정 사건을 기반으로 동작하도록 설계하는 프로그래밍 방식입니다.

이벤트 기반 프로그래밍에서는 이벤트가 발생하면 이를 처리하기 위해 콜백 함수를 호출하는 방식으로 동작합니다.

이벤트 처리를 비동기적으로 처리함으로써, 블로킹되지 않고, 여러 이벤트를 동시에 처리할 수 있습니다.

 

Node.JS를 사용하는 이유는?

Node.JS는 자바스크립트를 통해 프런트와 서버단 로직을 모두 처리할 수 있습니다.

이벤트 기반 프로그래밍과 비동기 I/O 처리를 통해 효율적인 작업이 가능합니다.

npm을 이용하여 모듈을 공유하고 재사용할 수 있어 개발 효율성이 증가합니다.

profile

Woozy_DevLog

@Woozy_Dev

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!