Node.js 마스터 클래스 Ep 5. Node.js의 심장, 이벤트 루프와 워커 스레드

Ep 5. Node.js의 심장, 이벤트 루프와 워커 스레드

"Node.js는 싱글 스레드(Single-thread)라서 느리다?" 이것은 반은 맞고 반은 틀린 이야기입니다. Node.js는 싱글 스레드지만, 아주 바쁜 지배인(이벤트 루프)과 든든한 일꾼들(워커 스레드) 덕분에 그 어떤 서버보다 빠르게 동작할 수 있습니다.

---

1. 이벤트 루프 (Event Loop) 상세 분석

우리가 작성한 자바스크립트 코드는 메인 스레드에서 실행됩니다. 하지만 `setTimeout`, `fs.readFile` 같은 작업은 메인 스레드가 아니라, 백그라운드(OS 커널이나 C++ 스레드 풀)에 맡겨집니다.

이벤트 루프는 메인 스레드가 할 일이 없을 때, 백그라운드에서 완료된 작업들을 가져와서 실행하는 역할을 합니다. 마치 회전초밥집의 레일처럼 끊임없이 돌면서(Loop) 이벤트를 전달하죠.

이벤트 루프의 6단계 (Phases)

(깊게 들어가면 복잡하지만, 3가지만 기억하세요!)

1. Timer Phase: `setTimeout`, `setInterval` 콜백을 실행합니다.

2. Poll Phase: 대부분의 I/O 작업(파일 읽기, 네트워크 요청 등)을 처리합니다.

3. Check Phase: `setImmediate` 콜백을 실행합니다.

2. 싱글 스레드의 한계와 워커 스레드 (Worker Threads)

Node.js는 I/O 작업(네트워크, 파일)에는 최강이지만, CPU를 많이 쓰는 작업(이미지 처리, 복잡한 수학 연산, 암호화)에는 약점이 있습니다. 계산하느라 바빠서 다른 손님을 못 받기 때문이죠.

이럴 때 사용하는 것이 Worker Threads입니다. 메인 스레드 외에 별도의 스레드를 만들어 무거운 작업을 위임하는 것이죠.

---

🛠️ 오늘의 미션: 소수(Prime Number) 찾기 대결

CPU를 엄청나게 쓰는 작업인 '소수 찾기'를 메인 스레드에서 할 때와 워커 스레드에서 할 때의 차이를 느껴봅시다.

1. `worker-test.js` 파일을 만듭니다.

2. 아래 코드를 작성합니다.

const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
  // === 메인 스레드 ===
  console.log('메인 스레드 시작');
  
  // 워커 스레드 생성
  const worker = new Worker(__filename);
  
  worker.on('message', (msg) => {
    console.log('워커로부터 받은 메시지:', msg);
  });
  
  console.log('메인 스레드는 다른 일을 계속합니다...');
  
} else {
  // === 워커 스레드 ===
  // 시간을 오래 끄는 작업 (소수 찾기 시늉)
  let count = 0;
  for (let i = 0; i < 1000000000; i++) {
    count++;
  }
  
  parentPort.postMessage(`작업 완료! 10억 번 셌습니다.`);
}

3. `node worker-test.js`를 실행해 보세요.

4. "메인 스레드는 다른 일을 계속합니다..."가 먼저 출력되고, 잠시 뒤에 "작업 완료!"가 뜰 겁니다. 메인 스레드가 멈추지 않고 계속 일했다는 증거죠!

---

🔗 다음 예고

이제 마지막 단계입니다. 수십 기가바이트의 거대한 파일도 메모리 걱정 없이 처리하는 Node.js의 필살기, "대용량 데이터 처리: Stream과 Buffer"로 대단원의 막을 내리겠습니다.