Steam-ad-poc 번외 Node.js의 동시성

M4r()·2025년 8월 14일
0

steam-ad-poc

목록 보기
3/3
post-thumbnail

Node.js

Node.js는 단일 스레드로 동작하지만 이벤트 루프를 이용한 논블로킹 I/O를 지원하고, 코드 작성 시에는 Promise 객체를 활용한 비동기 처리를 async / await를 사용해 간단하게 처리할 수 있습니다. 이런 간단한 처리로 런타임이 많은 부분을 알아서 효율적으로 처리해주기 때문에 저는 지금까지 동시성이나 병렬성 때문에 대해 고민할 필요성을 느끼지 못했습니다.
하지만 Python의 동시성과 병렬성에 대해 찾아보면서 지금까지 써왔던 Node.js의 비동기 처리와 비교하며 이해하는 것이 편해 함께 찾아본 내용을 기록으로 남깁니다.

특징과 동작방식

  • JS 코드는 별도 설정이 없으면 메인 스레드에서 실행된다.
  • 메인 스레드 외에 libuv 스레드풀이 있어, 비동기 API 호출 시 작업 종류에 따라 스레드풀로 오프로드 하거나 OS 커널의 비동기 I/O 에 등록한다.
    (참고: JS 코드를 병렬로 돌리는 용도는 worker_threads이며, libuv 스레드풀과는 역할이 다름)
  • 콜 스택(V8 실행 영역), 페이즈 별로 나뉜 여러 태스크 큐, 마이크로 태스크 큐가 있고, 이벤트 루프가 큐를 스케줄링해 콜 스택으로 올린다.
  • 함수 실행 중 비동기 작업이 필요하면 작업을 커널/libuv에 등록하고 현재 콜 스택을 비운다.
  • 콜 스택이 비면 이벤트 루프가 규칙에 따라 태스크 큐에 큐잉 된 작업들을 콜 스택에 등록해 처리한다.

강점

I/O 바운드(네트워크·디스크) 에 매우 효율적. 사용자 코드는 블로킹 호출만 피하면 높은 처리량을 낼 수 있다.

주의

CPU 바운드 연산은 이벤트 루프를 블로킹한다 → worker_threads 로 분리하거나 네이티브 애드온으로 우회해야 한다.

Promise와 콜백

함수나 라이브러리에 따라서 실행 결과를 Promise로 반환하는 방식을 사용하기도 하고, 결과를 인자로 넘긴 콜백을 호출해 알려주기도 한다.
async / await는 Promise를 활용하기 때문에 콜백 기반의 API에 await를 사용하고 싶다면 Promise로 래핑하는 과정이 필요하다.
콜백은 매크로 태스크로 큐잉되고, Promise가 resolve된 결과는 마이크로 태스크로 큐잉 되어 동작 순서에 영향을 미치는 등의 차이가 있다.

이벤트 루프의 스케쥴링

콜 스택이 비었을 때 이벤트 루프가 다음 작업을 선택하는 규칙은 생각보다 규칙이 많아서 다음 기회에 정리하고자 합니다.

profile
달리려고 해야 걸을 수 있다.

0개의 댓글