728x90
반응형
1. 이벤트 루프란
이벤트 루프는 Node.js가 단일 스레드임에도 불구하고 비동기 작업을 처리할 수 있게 해주는 핵심 메커니즘입니다. JavaScript 코드 실행, 콜백 처리, 네트워크 I/O, 타이머 등의 작업을 조율하며, Node.js의 논블로킹 I/O 모델의 근간이 됩니다.
이벤트 루프는 libuv 라이브러리에 의해 구현되어 있으며, 운영체제의 커널 기능을 활용하여 효율적인 비동기 처리를 수행합니다.
2. 이벤트 루프의 단계(Phases)
이벤트 루프는 6개의 단계를 순환하며 실행됩니다. 각 단계는 실행할 콜백 큐를 가지고 있습니다.
- timers: setTimeout(), setInterval() 콜백 실행
- pending callbacks: 이전 루프에서 지연된 I/O 콜백 실행
- idle, prepare: 내부적으로 사용되는 단계
- poll: 새로운 I/O 이벤트 처리, I/O 관련 콜백 실행
- check: setImmediate() 콜백 실행
- close callbacks: socket.on('close') 같은 close 이벤트 콜백 실행
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
3. 주요 비동기 함수의 실행 순서
3.1 setTimeout vs setImmediate
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
// 실행 순서는 상황에 따라 다를 수 있음
// I/O 콜백 내부에서는 setImmediate가 항상 먼저 실행됨
I/O 콜백 내부에서 실행할 경우:
const fs = require('fs');
fs.readFile('file.txt', () => {
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
});
// 출력 순서: setImmediate → setTimeout (항상 동일)
3.2 process.nextTick과 Promise
process.nextTick()과 Promise는 이벤트 루프의 단계와 별개로 마이크로태스크 큐에서 처리됩니다.
console.log('1: 동기 코드');
setTimeout(() => console.log('2: setTimeout'), 0);
Promise.resolve().then(() => console.log('3: Promise'));
process.nextTick(() => console.log('4: nextTick'));
console.log('5: 동기 코드');
// 출력 순서: 1 → 5 → 4 → 3 → 2
실행 우선순위:
- 동기 코드 (Call Stack)
- process.nextTick() 큐
- Promise 마이크로태스크 큐
- 이벤트 루프 단계별 큐 (timers, poll, check 등)
반응형
4. 이벤트 루프 블로킹 주의사항
4.1 블로킹 코드 예시
// 잘못된 예: 이벤트 루프를 블로킹하는 코드
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 이 작업 동안 다른 모든 요청이 대기 상태가 됨
app.get('/slow', (req, res) => {
const result = fibonacci(45); // 수 초 소요
res.json({ result });
});
4.2 블로킹 방지 방법
// 방법 1: setImmediate로 작업 분할
function processLargeArray(array, callback) {
const chunk = 1000;
let index = 0;
function processChunk() {
const end = Math.min(index + chunk, array.length);
for (; index < end; index++) {
// 작업 수행
}
if (index < array.length) {
setImmediate(processChunk);
} else {
callback();
}
}
processChunk();
}
// 방법 2: Worker Threads 사용 (CPU 집약적 작업)
const { Worker } = require('worker_threads');
function runFibonacci(n) {
return new Promise((resolve, reject) => {
const worker = new Worker('./fibonacci-worker.js', {
workerData: n
});
worker.on('message', resolve);
worker.on('error', reject);
});
}
5. 실전 활용 예시
5.1 비동기 작업 순서 제어
async function orderedExecution() {
console.log('1: 시작');
await new Promise(resolve => {
setImmediate(() => {
console.log('2: setImmediate');
resolve();
});
});
await new Promise(resolve => {
setTimeout(() => {
console.log('3: setTimeout');
resolve();
}, 100);
});
console.log('4: 완료');
}
orderedExecution();
// 출력: 1 → 2 → 3 → 4
결론
Node.js의 이벤트 루프는 단일 스레드 환경에서 비동기 작업을 효율적으로 처리하는 핵심 메커니즘입니다. timers, poll, check 등의 단계를 순환하며 콜백을 실행하고, process.nextTick과 Promise는 마이크로태스크로 우선 처리됩니다. CPU 집약적 작업으로 이벤트 루프를 블로킹하지 않도록 주의하고, 필요시 Worker Threads를 활용하는 것이 중요합니다.
728x90
반응형
'Node.js' 카테고리의 다른 글
| Node.js의 비동기 프로그래밍(Asynchronous Programming) (0) | 2026.02.21 |
|---|---|
| Node.js 설치 및 설정 방법 (0) | 2026.02.21 |
| Node.js란 무엇인가 (0) | 2026.02.21 |