1. 타이머 모듈: 비동기 작업의 핵심
Node.js의 타이머 모듈은 코드 실행을 미래 시점으로 예약하는 전역 API를 제공합니다. setTimeout, setInterval과 같은 함수는 브라우저 환경에도 존재하지만, Node.js에서는 이벤트 루프의 영향을 직접 받아 동작 방식이 다릅니다.
2. 기본 스케줄러: setTimeout과 setInterval
2.1 setTimeout(callback, delay[, ...args])
delay 밀리초 후에 콜백을 한 번 실행합니다. delay는 정확한 시간이 아닌 최소 대기 시간이며, 이벤트 루프의 상태에 따라 실제 실행 시점이 달라질 수 있습니다.
const delayedAction = setTimeout(() => {
console.log('1000ms 이상 지연된 메시지');
}, 1000);
2.2 setInterval(callback, delay[, ...args])
delay 간격으로 콜백을 반복 실행합니다. 주의: 콜백 실행 시간이 delay를 초과하면 작업이 연속적으로 쌓여 성능 문제가 발생할 수 있습니다.
let executionCount = 0;
const repeater = setInterval(() => {
console.log('반복 실행:', ++executionCount);
if (executionCount >= 3) {
clearInterval(repeater);
}
}, 1000);
3. 고급 스케줄러: setImmediate와 process.nextTick
3.1 setImmediate(callback[, ...args])
현재 이벤트 루프의 폴링 단계 종료 후 체크 단계에서 실행됩니다. I/O 작업이 완료된 후 "즉시" 실행되지만, 현재 작업의 완료를 기다립니다.
3.2 process.nextTick(callback[, ...args])
현재 작업 종료 후 이벤트 루프의 다음 단계 진입 전에 실행됩니다. setImmediate보다 우선순위가 높으며 현재 단계의 마지막에서 트리거됩니다.
4. 실행 순서의 핵심 원리
다양한 스케줄링 메서드의 실행 우선순위:
process.nextTick(마이크로태스크, 현재 작업 직후 실행)Promise.then()(마이크로태스크, nextTick 큐 이후 실행)setImmediate(매크로태스크, 체크 단계 실행)setTimeout(fn, 0)(매크로태스크, 타이머 단계 실행)
console.log('시작');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick 1'));
Promise.resolve().then(() => console.log('Promise 1'));
console.log('종료');
예상 출력:
시작 종료 nextTick 1 Promise 1 setTimeout setImmediate
5. 실무 적용 사례
5.1 이벤트 루프 블로킹 방지
대규모 데이터 처리 시 setImmediate를 이용한 작업 분할:
function chunkProcessor(data, handler, completion) {
let position = 0;
function executeSlice() {
const sliceStart = Date.now();
while (position < data.length && Date.now() - sliceStart < 5) {
handler(data[position], position);
position++;
}
if (position < data.length) {
setImmediate(executeSlice);
} else {
completion();
}
}
setImmediate(executeSlice);
}
5.2 unref()를 이용한 백그라운드 타이머
프로세스 종료를 방해하지 않는 타이머 생성:
const backgroundTimer = setTimeout(() => {
console.log('실행되지 않음');
}, 1000);
backgroundTimer.unref();
console.log('타이머 참조 해제로 인한 즉시 종료');
6. 핵심 원칙과 적용 가이드
- setTimeout/setInterval: 이벤트 루프 영향으로 정확성 보장 불가
- process.nextTick: 오류 처리 및 즉시 실행 작업에 적합
- setImmediate: 다음 이벤트 루프 사이클로 작업 연기 시 사용
- 마이크로태스크: 매크로태스크보다 항상 우선 실행
모범 사례:
setTimeout(fn, 0)대신setImmediate사용- CPU 집약적 작업 분할에
setImmediate적용 - 미사용 타이머 반드시 정리
- 백그라운드 작업에
unref()활용