Node.js - 이벤트 루프: 비동기 JavaScript의 마법을 밝혀내다

안녕하세요, 미래의 코딩 마법사 여러분! 오늘 우리는 Node.js의 핵심인 이벤트 루프로 흥미로운 여정을 떠납니다. 코드 한 줄도 작성해본 적 없다면 걱정하지 마세요; 이 fascineting 세계를 안내해 드릴 친절한 안내자가 바로 저입니다. 이 튜토리얼이 끝나면, Node.js가 많은 일을 동시에 어떻게 처리하는지 이해하게 될 것입니다. 마치 여러분이 숙제, 넷플릭스, 친구에게 메시지를 보내는 것처럼!

Node.js - Event Loop

이벤트 루프는 무엇인가?

여러분이 바쁜 식당의 주방장이라고 상상해 봅시다. 여러 가지 요리를 동시에 해 두고, 타이머가 틱틱거리고, 주문이 들어오고 있습니다. 요리를 타지 않고 고객을 기다리게 하지 않고 모든 것을 어떻게 관리할까요? 그것이 바로 Node.js의 이벤트 루프가 하는 일입니다!

이벤트 루프는 주방장처럼, 주의가 필요한 것을 끊임없이 확인하고 모든 것이 원활하게 돌아가도록 하는 것입니다. 이것이 Node.js가 비동기 I/O 연산을 수행할 수 있게 만드는 비밀의 재료입니다.

주요 개념

더 깊이 다들기 전에, 몇 가지 주요 개념을 알아보겠습니다:

  1. 단일 스레드: JavaScript는 단일 스레드에서 실행되며, 동시에 하나의 작업만 수행할 수 있습니다.
  2. 비 블록킹: Node.js는 각 작업이 끝나기 전에 다음 작업으로 이동할 수 있도록 여러 작업을 동시에 처리할 수 있습니다.
  3. 비 동기: 작업은 지금 시작하고 나중에 완료할 수 있어, 다른 코드가 동시에 실행될 수 있습니다.

이벤트 루프는 어떻게 작동하나?

이벤트 루프를 소화하기 쉽게 나누어 설명하겠습니다:

  1. 호출 스택에서 동기 코드 실행
  2. 타이머 확인 (setTimeout, setInterval)
  3. 대기 중인 I/O 작업 확인
  4. setImmediate 콜백 실행
  5. 'close' 이벤트 처리

이제 코드 예제로 이를 구현해 보겠습니다!

예제 1: 동기 대비 비 동기 코드

console.log("First");

setTimeout(() => {
console.log("Second");
}, 0);

console.log("Third");

출력이 어떤 것일까요? 설명해 보겠습니다:

  1. "First"는 즉시 로그됩니다.
  2. setTimeout을 만나지만, 기다리지 않고 타이머를 설정하고 계속 진행합니다.
  3. "Third"는 로그됩니다.
  4. 이벤트 루프는 완료된 타이머를 확인하고 콜백을 실행하여 "Second"를 로그합니다.

출력:

First
Third
Second

놀라셨나요? 이는 Node.js가 비동기 작업을 처리하면서 주요 스레드를 블록하지 않는 방법을 보여줍니다.

예제 2: 여러 타이머

setTimeout(() => console.log("Timer 1"), 0);
setTimeout(() => console.log("Timer 2"), 0);
setTimeout(() => console.log("Timer 3"), 0);

console.log("Hello from the main thread!");

이 예제에서는 여러 타이머를 설정합니다. 그러나 이벤트 루프는 여전히 주요 스레드가 끝나기를 기다립니다.

출력:

Hello from the main thread!
Timer 1
Timer 2
Timer 3

이벤트 루프의 단계

이벤트 루프를 실제로 보았으니, 단계를 더 자세히 탐구해 보겠습니다:

1. 타이머 단계

이 단계에서 setTimeout()와 setInterval()에 의해 예약된 콜백이 실행됩니다.

setTimeout(() => console.log("I'm a timer!"), 100);
setInterval(() => console.log("I repeat every 1 second"), 1000);

2. 대기 중인 콜백 단계

이 단계에서 다음 루프 이터레이션으로 이동된 I/O 콜백이 실행됩니다.

3. 대기, 준비 단계

내부 사용만. 여기서 볼 것은 없습니다!

4. 폴 단계

새로운 I/O 이벤트를 검색하고 I/O 관련 콜백을 실행합니다.

const fs = require('fs');

fs.readFile('example.txt', (err, data) => {
if (err) throw err;
console.log(data);
});

5. 체크 단계

setImmediate() 콜백이 여기서 호출됩니다.

setImmediate(() => console.log("I'm immediate!"));

6. 클로즈 콜백 단계

일부 클로즈 콜백, 예를 들어 socket.on('close', ...)가 여기서 처리됩니다.

모든 것을 통합하다

여러 가지 이벤트 루프 요소를 사용하는 더 복잡한 예제를 만들어 보겠습니다:

const fs = require('fs');

console.log("Start");

setTimeout(() => console.log("Timeout 1"), 0);
setImmediate(() => console.log("Immediate 1"));

fs.readFile('example.txt', (err, data) => {
console.log("File read complete");
setTimeout(() => console.log("Timeout 2"), 0);
setImmediate(() => console.log("Immediate 2"));
});

console.log("End");

실행 순서는 놀라울 수 있습니다:

  1. "Start"와 "End"는 즉시 로그됩니다.
  2. 첫 번째 setTimeout과 setImmediate가 큐에 추가됩니다.
  3. 파일 읽기 작업이 시작됩니다.
  4. 이벤트 루프는 다음 주기를 시작합니다:
  • 첫 번째 setTimeout 콜백이 실행됩니다.
  • 첫 번째 setImmediate 콜백이 실행됩니다.
  • 파일 읽기가 완료되면 그 콜백이 실행됩니다.
  • 파일 읽기 콜백 내부에서 다른 setTimeout과 setImmediate가 큐에 추가됩니다.
  • 두 번째 setImmediate는 두 번째 setTimeout보다 먼저 실행됩니다.

일반 이벤트 루프 메서드

다음은 Node.js에서 일반적으로 사용되는 이벤트 루프 관련 메서드 표입니다:

메서드 설명
setTimeout(callback, delay) delay 밀리초 후 callback을 실행합니다
setInterval(callback, interval) interval 밀리초 마다 callback을 반복적으로 실행합니다
setImmediate(callback) 다음 이벤트 루프 이터레이션에서 callback을 실행합니다
process.nextTick(callback) 현재 작업이 완료된 후 callback을 '다음 틱 큐'에 추가합니다

결론

축하합니다! 여러분은 Node.js와 그 이벤트 루프의 fascineting 세계로 첫 걸음을 뗐습니다. 자전거 타는 것을 배우는 것처럼, 비동기 프로그래밍을 마스터하려면 연습이 필요합니다. 바로 이해되지 않는다면 실망하지 마세요 - 계속 실험하고 곧 비동기 코드를 프로처럼 작성하게 될 것입니다!

마무리로 재미있는 비유를 드리자면: 이벤트 루프를 카르ousel로 생각해 보세요. 다양한 작업(타이머, I/O 연산, 즉시 콜백)은 아이들이 카르ousel에 탑승하려고 하는 것처럼 생각해 보세요. 이벤트 루프는 끊임없이 회전하며, 특정 순서로 작업을 집어넣고 내려놓아 모두가 차례대로 돌아가도록 합니다.

계속 코딩하시고, 호기심을 유지하시고, 기억하세요 - Node.js의 세계에서, 인내는 단지 콜백입니다!

Credits: Image by storyset