Node.js에서 브라우저 WebAPI의 일종인 setTimeout을 어떻게 사용할 수 있을까?

이동준·2023년 9월 10일

노드

목록 보기
2/2

node.js에서 브라우저의 WebAPI의 일종인 setTimeout을 어떻게 사용할 수 있는건지에 대한 답변을 하기 위해선 node.js가 비동기 작업을 어떻게 처리하는지, 이벤트 루프를 알 필요가 있다.

Node.js Event Loop

node.js는 싱글 스레드 논 블로킹이라고 한다. 하나의 스레드로 동작하지만 I/O 작업이 발생한 경우 이를 비동기적으로 처리하기 때문에 그렇게 말하는 것이다. 하나의 스레드는 하나의 실행 흐름만을 가지고 있고 파일 읽기와 같이 기다려야하는 작업을 실행하게되면 그 작업이 끝나기 전에는 아무것도 할 수 없어야 정상이다. 하지만 node.js는 하나의 스레드만으로 여러 비동기 작업들을 블로킹 없이 수행할 수 있고 그 기반에는 이벤트 루프가 핵심적인 역할을 한다.

Node.js의 구조

이벤트 루프를 이해하기 위해서 node.js가 어떻게 구성되어 있는지 알아야 한다. node.js는 c++로 작성된 런타임이고, 그 내부에 V8 엔진을 가지고 있다. 그 덕분에 크롬과 같은 브라우저 환경 밖에서도 실행이 가능해진 것이다. 그런데 그 내부에는 V8 엔진 말고도 libuv라는 라이브러리가 존재한다.

libuv

리버브란 C++로 작성된 node.js가 사용하는 비동기 I/O 라이브러리이다. 이는 사실 운영체제의 커널을 추상화한 Wrapping 라이브러리로 커널이 어떤 비동기 API를 지원하는지 알고있다.

우리가 리버브에게 파일 읽기와 같은 비동기 작업을 요청하면 리버브는 이 작업을 커널이 지원하는지 확인한다. 지원한다면 리버브가 대신 커널에게 비동기적으로 요청했다가 응답이오면 그 응답을 우리에게 전달해준다. 만약 요청한 작업을 커널이 지원하지 않는다면 자신만의 워커 스레드가 담긴 스레드 풀을 사용한다.

리버브는 기본적으로 4개의 스레드를 가지는 스레드 풀을 생성한다. uv_threadpool라는 환경 변수를 설정해 최대 128개까지 스레드 개수를 늘릴 수도 있다. 만약 우리가 요청한 작업을 커널이 지원하지 않는다면 리버브는 커널을 호출하는 대신 이 스레드 풀에게 작업을 맡겨버린다.

그래서?

그래서 리버브와 이벤트 루프는 어떠한 관계를 가지고 있고 node.js는 어떻게 싱글 스레드로 논블로킹 비동기 작업이 가능한 것일까?

node.js는 I/O 작업을 자신의 메인 스레드가 아닌 다른 스레드에 위임함으로써 싱글 스레드로 논 블로킹 I/O를 지원한다. 다르게 말하면 node.js는 I/O 작업을 리버브에게 위임함으로써 논 블로킹 I/O를 지원하고 그 기반에는 이벤트 루프가 존재한다.

Node.js의 이벤트 루프


점선으로 된 nextTickQueue와 microTaskQueue는 이벤트 루프의 일부가 아니다.
이벤트 루프는 node.js가 여러 비동기 작업을 관리하기 위한 구현체다. 비동기 작업들을 모아서 관리하고 순서대로 실행 할 수 있게 해주는 도구이며 위와 같이 구성되어있다.

각 박스는 특정 작업을 수행하기 위한 페이즈(Phase)를 의미한다.

  • Timer Phase: 타이머와 관련된 콜백 처리
  • Pending Callbacks Phase: 이전 루프에서 완료된 콜백 또는 에러 콜백 등 처리
  • Idle, Prepare Phase: 이벤트 루프가 순회할 때마다 실행되며, Poll Phase에 준비 작업을 하는 단계
  • Poll Phase: 대기중인 콜백을 콜 스택으로 가장 많이 올려보내는 단계
  • Check Phase: setImmediate 함수만을 위한 단계
  • Close Callbacks Phase: close type의 콜백을 관리하는 단계

페이즈의 실행 순서는 Timer Phase부터 순서대로 순회한다. 다음 페이즈로 넘어가는 과정을 틱이라고 부른다.
각 Phase는 Queue로 이루어져 있고, Queue에는 사용자가 등록한 Callback들이 알맞게 담겨서 자신의 실행을 기다리게 된다.
각 Phase는 자신 Queue의 모든 일을 수행하거나, 제한 갯수까지 실행한 후에 다음 Phase로 이동하게 된다.

이 중 setTimeout은 Timer Phase에서 실행되게 된다.

0개의 댓글