오늘 뭐했냐/개발에 대한 주저리

23.10.11 Node.js에서 비동기 처리

스스로에게 2023. 10. 15. 15:05

Node.js는 JS를 기반으로 한 싱글 스레드를 사용하는데 어떻게 비동기처리가 가능할까 하는 의문에서 찾아봤다.

비동기 처리를 위한 주요 요소

  1. 싱글 스레드 기반의 이벤트 루프:
    • Node.js는 기본적으로 싱글 스레드로 동작합니다.
    • 이벤트 루프는 콜 스택이 비어있을 때 콜백 큐에서 콜백 함수를 가져와 실행합니다. 이러한 방식으로 비동기 이벤트에 반응합니다.

  2. libuv 라이브러리:
    • Node.js는 비동기 I/O 연산을 지원하기 위해 libuv 라이브러리를 사용합니다.
    • libuv는 백그라운드에서 여러 스레드를 가진 스레드 풀을 관리하여 I/O 작업을 병렬로 처리합니다.
    • I/O 작업이 완료되면, 해당 작업과 관련된 콜백이 콜백 큐에 추가됩니다.

  3. 비동기 I/O:
    • Node.js는 파일 시스템 작업, 네트워크 요청 등 대부분의 I/O 작업을 비동기 방식으로 처리합니다.
    • 싱글 스레드에서 I/O 작업을 기다리는 동안 다른 작업을 중단하지 않습니다. 대신, I/O 작업을 백그라운드로 보내고, 작업이 완료되면 콜백 함수를 실행합니다.

  4. 콜백 및 프로미스 기반 API:
    • Node.js는 비동기 작업을 위해 콜백 기반 API를 제공합니다.
    • 최근에는 프로미스나 async/await와 같은 추상화를 통해 더 편리하고 가독성 높은 비동기 코드 작성을 지원하고 있습니다.

  5. 이벤트 주도 아키텍처:
    • Node.js는 이벤트 주도 아키텍처를 채택하고 있습니다.
    • EventEmitter 클래스를 통해 객체가 이벤트를 발행하고 구독하는 것이 가능하며, 이는 비동기 프로그래밍 패턴에 적합합니다.

libuv 라이브러리 이건 뭐지?

  1. libuv 라이브러리:
    • Node.js에서 I/O 작업을 처리하는 주요 구성 요소는 libuv 라이브러리입니다.
    • libuv는 비동기 I/O 작업을 지원하기 위해 설계되었으며, 내부적으로는 여러 백그라운드 스레드를 사용하는 스레드 풀을 관리합니다.
    • Node.js가 비차단 I/O 작업을 요청하면, 해당 작업은 libuv의 스레드 풀에 의해 백그라운드에서 처리됩니다. 작업이 완료되면, 결과는 이벤트 루프를 통해 메인 스레드에 전달되며, 관련 콜백 함수가 실행됩니다.

  2. 특징 및 역할
    1. 플랫폼 독립성libuv는 다양한 운영 체제에서 동작할 수 있도록 설계되었습니다. 이를 위해 OS 별로 다른 비동기 I/O 메커니즘을 추상화하여 제공합니다.
    2. 이벤트 루프libuv는 이벤트 기반 프로그래밍을 지원하기 위한 이벤트 루프를 제공합니다. 이 이벤트 루프를 통해 Node.js는 비동기 I/O 연산의 결과를 체크하고 콜백 함수를 실행합니다.
    3. 스레드 풀libuv는 내부적으로 고정 크기의 스레드 풀을 가지고 있습니다. 이 스레드 풀은 비동기 I/O를 직접 지원하지 않는 작업 (예: 일부 파일 시스템 연산) 및 CPU 집약적인 작업을 병렬로 처리하는 데 사용됩니다.
    4. 타이머, TCP, UDPlibuv는 타이머, TCP, UDP와 같은 다양한 유형의 연산을 지원합니다. 이를 통해 Node.js는 네트워크 연결, 데이터 전송, 일정한 간격으로 작업 실행 등의 기능을 제공합니다.
    5. 비동기 I/O: 운영 체제가 비동기 I/O를 지원하는 경우, libuv는 해당 기능을 활용합니다. 운영 체제가 해당 기능을 지원하지 않는 경우, libuv는 자체 스레드 풀을 사용하여 비동기 I/O를 에뮬레이트합니다.
    6. FS 이벤트, 시그널: 파일 시스템의 변경 사항을 감지하거나 OS 시그널을 처리하는 기능도 libuv에 포함되어 있습니다.

Node.js는 싱글 스레드 아닌가? 멀티 스레드가 어떻게 가능하지?

 Node.js의 메인 실행 컨텍스트, 즉 JavaScript 코드를 실행하는 부분은 실제로 싱글 스레드입니다. 그러나 Node.js의 전체 아키텍처는 단순히 싱글 스레드만을 사용하는 것이 아닙니다. libuv 라이브러리는 이와 별개로 동작합니다.

 

그런데 JS도 비동기 문법이 있는데 그건 어떻게 되는거지?

JavaScript의 비동기 처리 기능 자체는 언어의 스펙에 포함되어 있습니다. 그러나 JavaScript가 실행되는 환경, 즉 런타임(예: 브라우저나 Node.js)은 해당 비동기 기능을 실제로 구현하는 데 필요한 메커니즘과 도구를 제공합니다.

 

예를 들어:

  1. 브라우저에서의 비동기 처리:
    • XMLHttpRequest나 Fetch API는 웹 API로 제공되며, 이를 통해 비동기적으로 네트워크 요청을 수행할 수 있습니다.
    • setTimeoutsetInterval 또한 브라우저 환경에서 제공되는 타이머 관련 함수로, 지정된 시간 후에 콜백을 실행하는 데 사용됩니다.
    • requestAnimationFrame은 애니메이션과 관련된 비동기 작업을 수행하는 데 사용되는 함수입니다.

  2. Node.js에서의 비동기 처리:
    • fs.readFile과 같은 파일 시스템 함수나 네트워크 관련 함수들은 libuv 라이브러리와 같은 외부 도구를 통해 비동기적으로 동작합니다.
    • setTimeoutsetInterval 함수도 Node.js에서 사용 가능하며, 이는 libuv를 통해 구현됩니다.

즉, JavaScript 언어 자체는 비동기 작업을 위한 콜백, 프로미스, async/await와 같은 패턴과 구조를 제공하지만, 실제로 이러한 비동기 작업들이 어떻게 수행되는지는 실행 환경(브라우저, Node.js 등)에 따라 다르며, 이러한 환경이 해당 기능의 구체적인 구현을 제공합니다.

 

정리하면 

 JS에서 비동기 처리를 위한 패턴과 구조는 제공하지만 실제 작동은 실행 환경에 따라 다르게 구현된다. Node.js에서는 libuv 라이브러리를 통해서 구현된다. 그리고 libuv가 운영체제별 I/O 메커니즘에 대한 추상화를 제공하며, 이를 통해 Node.js는 다양한 플랫폼에서 동일한 코드로 비동기 I/O 연산을 수행할 수 있다.