포도가게의 개발일지

[Node] 작동 원리 본문

[JS,TS,Node]

[Node] 작동 원리

grape.store 2022. 2. 9. 18:54
반응형

Node.js

- Node.js C++로 작성된 런타임이고 그 내부에 V8 Engine 가지고 있다. 그 덕분에 크롬과 같은 브라우저에서 실행하던 자바스크립트를 로컬에서 실행할 수 있다. 그런데 그 내부에는 V8 Engine 말고도 libuv 라는 라이브러리가 존재한다.

Node 구조
브라우저 구조 call stack / heap은 v8엔진 내부에 있다.

 

- Node.js 싱글 스레드 논 블로킹(제어권을 넘기지 않고 가지고 있는다.)이라고 한다. 또한 싱글 스레드로 작동하므로 I/O같은 작업은 비동기적(작업이 완료되었는지 여부를 확인하지 않고 callback처리한다.)으로 처리해야 한다. event loop에 멈춤이 없어야 한다(Node.js 최대한 모든 작업을 비동기하게 처리해야 한다.)

 

libuv

- libuv C++로 작성된, Node.js가 사용하는 비동기 I/O 라이브러리다. 이는 사실 운영체제의 커널추상화한 Wrapping 라이브러리로 커널 어떤 비동기 API를 지원하는지 알고있다. 때문에 어떠한 작업이 비동기로 처리해야하는지 알고 있으며 해당 작업이 요청이 들어왔을시 libuv가 알고 있는 비동기 작업(즉 커널이 처리할 수 있는 작업)이라면 커널(os)에 요청하며 커널은 해당작업을 수행하게 된다.

 

- 만약 지원하지 않으면 Thread Pool에 미리 만들어 놓은 워커 스레드에게 작업을 맡기게 된다.

 

- 결론적으로 Node.js는 I/O작업을 메인 스레드로 진행하지 않고 다른 스레드에게 위임함으로써 논 블로킹을 유지한다.

 

이벤트루프

https://www.korecmblog.com/node-js-event-loop/#nodsjs-%EC%86%8D-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84

이벤트 루프의 작업 흐름

우리가 test.js를 콘솔에서 실행시켰을 때, Node.js는 이벤트 루프를 생성한 다음 이벤트 루프 바깥에서 V8엔진을 이용하여 메인 모듈인 test.js를 실행한다. 한번 메인 모듈이 실행되고나면 Node.js는 이벤트 루프가 활성 상태인지, 즉 이벤트 루프 안에서 해야할 작업이 있는지를 확인한다. 만약 이벤트 루프를 돌릴 필요가 없다면 Node.js는 process.on('exit, () => {})를 실행하고 이벤트 루프를 종료하려고 할 것이다. 

 

그러나 만약 이벤트 루프를 돌려야할 상황이라면 Node.js는 V8엔진을 이용하여 이벤트 루프의 첫 번째 페이즈인 Timer phase를 실행한다.

1. Timer Phase

- Timer Phase는 말 그대로 setTimeout이나 setInterval과 같은 함수가 만들어 내는 타이머들을 다룬다. 

2. Pending Callbacks Phase

- 이 큐에 담기는 콜백들은 이전 이벤트 루프 반복에서 수행되지 못했던 I/O 콜백들이다. 대부분의 페이즈는 시스템 실행 한도의 영향을 받는다. 때문에 큐에 쌓인 모든 작업을 실행하지 못하고 다음 페이즈로 넘어갈 수도 있다. 이때 처리하지 못하고 넘어간 작업들을 쌓아놓고 실행하는 페이즈이다.

3. Idle, Prepare Phase

- ??? 내부적으로 사용

4. Poll Phase

- 새로운 I/O 이벤트를 다룹니다. setTimeout, setImmediate, close 콜백 등을 제외한 모든 콜백이 여기서 실행된다고 생각하면 된다.

- 특이하게 다른 Phase와 모든 페이즈에 queue를 확인하여 달리 처리해야 할 작업이 없으면 일정시간 동안 대기하다가 새로운 작업이 들어오는 순간 즉시 실행한다.

  • 데이터베이스에 쿼리를 보낸 후 결과가 왔을 때 실행되는 콜백
  • HTTP 요청을 보낸 후 응답이 왔을 때 실행되는 콜백
  • 파일을 비동기로 읽고 다 읽었을 때 실행되는 콜백

5. Check Phase

- 이 페이즈는 오직 setImmediate의 콜백만을 위한 페이즈다.

  • process.nextTick은 같은 페이즈에서 호출한 즉시 실행된다.
  • setImmediate는 다음 틱에서 실행된다. 정확히는 Node.js가 틱을 거쳐 Check Phase에 진입하면 실행된다.

6. Close Callback Phase

- socket.on('close', () => {});과 같은 close 이벤트 타입의 핸들러를 처리하는 페이즈다.

nextTickQueue, microTaskQueue

  • 이벤트 루프의 일부가 아니다. libuv가 아닌 Node.js에 구현되어 있다. 따라서 이벤트 루프의 상관없이 동작한다.
  • nextTickQueue microTaskQueue는 현재 페이즈와 상관없이 지금 수행하고 있는 작업이 끝나면 그 즉시 바로 실행한다.
Comments