본문 바로가기
Nodejs

Node.js 알아보기

by teamnova 2022. 9. 3.
728x90

Node.js 공식 홈페이지
Node.js 구조

 

Node.js는 Chrome V8 Javascript 엔진으로 빌드된 JavaScript 런타임입니다.

크롬 V8 엔진이란 웹 브라우저를 만드는 데 기반을 제공하는 오픈 소스 자바스크립트 엔진을 뜻하며

런타임이란 특정 언어로 개발된 프로그램을 해석하고 실행할 수 있는 환경, 즉 자바스크립트가 해석하고 실행되는 환경을 뜻합니다.

Node.js가 등장하기 전의 자바스크립트는 브라우저 안에서만 동작 했었습니다.

2008년 구글이 크롬 브라우저를 출시하면서 V8 엔진을 개발, 이로 인해 자바스크립트의 실행속도가 대폭 개선되었습니다.

Node js의 구조는 위 사진과 같습니다. Node.js는 V8과 더불어 libuv라는 라이브러리를 사용합니다. V8과 libuv는 C와 C++로 구현되어 있으며, 자바스크립트 코드를 Node.js가 V8과 libuv에 연결해줍니다.

여기서 libuv 라이브러리는 Node.js의 특성인 이벤트 기반, 논블로킹 I/O모델을 구현하고 있습니다.

Node.js의 특징으로는 이벤트 루프, 싱글스레드, 논블로킹I/O 가 있습니다.

 

이벤트 루프(Event Loop)

 

  • 이벤트 발생 시 호출할 콜백 함수들을 관리
  • 호출된 콜백 함수의 실행 순서를 결정
  • Node.js가 실행 종료될 때까지 이벤트 처리를 위한 작업을 반복(loop)

 

 

 

 

                                                                                                                                     이벤트 루프의 작업 순서

 

Node.js는 이벤트 기반으로 클릭이나 네트워크 요청등 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해두고, 이를 이벤트 리스너에 콜백함수를 등록합니다.

이후 이벤트가 발생하면 리스너에 등록해둔 콜백함수를 호출하며, 이벤트가 끝난 후 Node.js는 다음 이벤트가 발생할 때까지 대기합니다.

Node.js는 하나의 스레드로 동작하지만 I/O 작업이 발생한 경우 이를 비동기적으로 처리할 수 있습니다. 

분명 하나의 스레드는 하나의 실행 흐름만을 가지고 있고 파일 읽기와 같이 기다려야 하는 작업을 실행하면 그 작업이 끝나기 전에는 아무것도 할 수 없어야만 합니다. 

그러나 Node.js는 하나의 스레드만으로 여러 비동기 작업들을 블로킹 없이 수행할 수 있고 그 기반에는 이벤트 루프가 존재합니다.

이벤트 루프란 여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백함수를 호출 할지를 이벤트 루프가 판단합니다.

노드는 이벤트가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프라고 부릅니다.

이벤트 루프의 동작 원리는 call stack과 callback queue를 지속적으로 감시(busy-wating)하면서 call stack이 빈 경우 callback queue에 작업이 있는지 확인하고 이를 call stack으로 옮겨오는 역할을 합니다.

 

 

 

싱글 스레드(Single Thread)

 

Node.js는 싱글스레드, 논 블로킹 모델로 싱글 스레드가 혼자서 일을 처리하지만 들어오는 요청 순서가 아닌 논 블로킹 방식으로 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행한다.

*Node.js가 싱글 스레드로 동작하지 않는 경우

  • 스레드풀 : 노드가 암호화, 파일 입출력, 압축 등 특정 동작 수행 시 스스로 멀티 스레드를 사용한다.
  • 워커 스레드 : 노드에서 멀티 스레드를 사용할 수 있게 된 기능으로 직접 다수의 스레드를 제어가능.  CPU 작업이 많은 경우 워커 스레드를 사용하며 노드 12버전에서 안정화된 기능이다.

싱글 스레드란 스레드가 하나뿐이라는 것을 의미합니다.

노드를 실행하면 프로세스가 하나 생성되는데 그 프로세스에서 스레드를 생성할 때 내부적으로는 스레드를 여러개 생성합니다. 하지만 직접 제어가 가능한 것은 스레드 하나뿐이기 때문에 노드가 싱글 스레드라고 하는 것입니다.

하나의 스레드만 제어가 가능하므로 많은 요청이 오면 하나씩 처리하고 논 블로킹 방식을 이용하여 대기 시간을 줄입니다.

 

Blocking(블로킹)과 Non-blocking(논블로킹)

 

마지막으로 노드js의 특징인 논블로킹 i/o 설명에 앞서 블로킹과 논블로킹의 차이에 대해 설명하겠습니다.

블로킹과 논블로킹은 A 함수가 B 함수를 호출했을 때, 제어권을 어떻게 처리하느냐에 따라 달라집니다.

Blocking

블로킹은

1. A 함수가 B 함수를 호출하면, 제어권을 A가 호출한 B 함수에 넘겨줍니다.

2. 제어권을 넘겨받은 B는 함수를 실행하고, A는 B에게 제어권을 넘겨주었기 때문에 함수 실행을 잠시 멈춥니다.

3. B함수는 실행이 끝나면 자신을 호출한 A에게 제어권을 돌려줍니다.

Non-blocking

 

논블로킹은 A함수가 B함수를 호출해도 제어권은 그대로 자신이 가지고 있습니다.

1. A함수가 B함수를 호출하면, B 함수는 실행되지만, 제어권은 A 함수가 그대로 가지고 있습니다.

2. A함수는 계속 제어권을 가지고 있기 때문에 B함수를 호출한 이후에도 자신의 코드를 계속 실행합니다.

 

 

좀 더 직관적으로 이해할 수 있게 코드 예시를 볼까요?

아래의 블로킹 예시에서는 file.md를 다 읽을 때까지 전체 JavaScript 시스템이 멈추지만,

논 블로킹 예시에서는 비동기적으로 실행되기에 전체 JavaScript 시스템이 멈추지 않고 moreWork()가 먼저 실행됩니다. 그리고 file.md를 다 읽고 나서 fs.readFile의 두 번째 파라미터로 들어간 콜백(callback) 함수가 실행됩니다.

여기서 콜백 함수란 어떤 이벤트(이 경우에서는 file I/O)가 끝나고 나서 실행되는 함수를 말합니다.

Blocking
Non-blocking

Synchronous(동기)와 Asynchronous(비동기)

그렇다면 동기와 비동기는 무엇일까요?

동기와 비동기의 차이는 호출되는 함수의 작업 완료 여부를 신경쓰는지의 여부의 차이입니다.

동기는 함수 A가 함수 B를 호출한 뒤, 함수 B의 리턴값을 계속 확인하면서 신경쓰는 것이 동기입니다.

비동기는 함수 A가 함수 B를 호출할 때 콜백 함수를 함께 전달해서, 함수 B의 작업이 완료되면 함께 보낸 콜백 함수를 실행합니다.

함수 A는 함수 B를 호출한 후로 함수 B의 작업 완료 여부에는 신경쓰지 않는것이 비동기입니다.

정리하자면 블로킹 논블로킹은 제어의 관점이라고 할 수 있고, 동기 비동기는 순서와 결과(처리)의 관점이라고 할 수 있습니다.

 

 

논블로킹(Non-blocking) I/O

  • 이벤트 루프를 잘 활용해 오래 걸리는 작업을 효율적으로 처리할 수 있다.
  • 동시에 실행될 수 있는 작업과 동시에 실행될 수 없는 작업이 있다.
  • 특히 파일 시스템 접근, 네트워크를 통한 요청 작업은 입력(Input)/출력(Output)의 일종이며, 이러한 작업을 할 때 노드는 비동기 방식으로 블로킹을 만들지 않게 끔(논 블로킹) 처리한다.
  • 함수 호출 시 바로 실행하는 것이 아니라(동기→블로킹) 일단 어느 곳에 쌓아 놓고 동시에 요청을 처리하고(비동기→논 블로킹) 요청이 완료된 순서대로처리(스택 이용) 한다.

이벤트 루프를 활용하면 작업 시간이 긴 것도 효율적으로 처리할 수 있는데, 작업에는 동시에 실행가능한 작업과 그렇지 않은 작업이 있습니다. 기본적으로 자바스크립트 상에서 돌아가는 것은 동시에 실행될 수 없지만 I/O 작업은 동시에 처리가 가능합니다. 이 때, 논 블로킹 방식으로 처리하는 것이 노드입니다.

논 블로킹으로 수행하기 위해서는 작업들이 모두 동시에 처리될 수 있는 작업이어야 합니다. 노드는 이러한 I/O 작업들을 백그라운드에서 동시에 처리하여 시간을 절약합니다. 이러한 작업 순서는 성능을 크게 좌우합니다. 

정리하자면 Node.js에서의 논블로킹 I/O 모델은 블로킹 작업(Input, Output과 관련된 작업 / http, Database CRUD, third party api, filesystem)들을 백그라운드(libuv의 스레드 풀)에서 수행하고, 이를 비동기 콜백함수로 이벤트 루프에 전달하는 것을 말합니다.

 

이상으로 Node.js의 특징에 대해 알아보았습니다.

 

 

출처 : https://yjp9603.tistory.com/