Node.js가 수천 개의 요청을 처리하는 방법
사람들은 Node.js가 싱글 스레드라고 말합니다.
하지만 Node.js는 멈추지 않고 수천 개의 요청을 처리하고, 파일을 읽고, API 호출을 수행합니다.
어떻게 가능한 걸까요?
정답은 libuv입니다.
libuv는 C 라이브러리입니다. 이는 Node.js에 비동기 및 논블로킹(non-blocking) I/O 기능을 제공합니다. JavaScript 자체로는 파일을 읽거나 네트워크 소켓을 관리할 수 없습니다. Node.js는 운영 체제와 통신하기 위해 libuv를 사용합니다.
libuv가 없다면 파일을 읽을 때마다 JavaScript는 멈추게 됩니다. 그러면 앱은 응답하지 않는 상태가 됩니다.
libuv가 있으면 파일 읽기는 백그라운드에서 진행됩니다. 그동안 JavaScript는 다른 코드를 계속 실행합니다.
예시:
console.log("Start");
fs.readFile("data.txt", "utf8", (err, data) => {
console.log(data);
});
console.log("End");
출력: Start End (파일 내용)
Node.js는 파일 읽기가 끝나기 전에 "End"를 출력합니다. 기다리지 않는 것이죠.
libuv가 코드를 관리하는 방식:
• 이벤트 루프(The Event Loop): libuv는 작업이 완료되었는지 확인하는 루프를 실행합니다. 작업이 완료되면 콜백을 큐(queue)에 넣습니다. • 스레드 풀(Thread Pool): JavaScript는 하나의 스레드를 사용합니다. 반면 libuv는 워커 스레드(worker threads) 풀을 사용합니다. 기본적으로 이 풀에는 4개의 스레드가 있습니다. 이 스레드들은 다음과 같은 무거운 작업들을 처리합니다:
- 파일 시스템 작업
- DNS 조회
- 압축
- 암호화 • 네트워킹(Networking): libuv는 HTTP, TCP, UDP 소켓을 관리합니다. 이를 통해 서버는 동시에 많은 연결을 처리할 수 있습니다. • 타이머(Timers): libuv는 setTimeout과 setInterval을 처리합니다.
실행 흐름은 다음과 같습니다:
- JavaScript가 코드를 실행합니다.
- libuv가 무거운 작업을 백그라운드로 가져갑니다.
- JavaScript는 다음 줄로 넘어갑니다.
- 작업이 완료되면 libuv가 이벤트 루프에 알립니다.
- 이벤트 루프가 콜백을 실행합니다.
피자 가게를 예로 들어보겠습니다.
libuv가 없다면, 여러분은 카운터 앞에 서서 10초마다 "제 음식 나왔나요?"라고 물어보게 됩니다. 그러면 줄이 막히게 됩니다.
libuv가 있다면, 주문을 하고 자리에 앉아 알림을 기다립니다. 피자가 준비될 때까지 다른 일을 자유롭게 할 수 있습니다.
요약:
• JavaScript 실행: V8 Engine • 이벤트 루프: libuv • 파일 I/O: libuv • 네트워크: libuv • 타이머: libuv
libuv는 Node.js를 빠르게 유지해 주는 엔진입니다.
