[Javascript] setInterval
정의
setInterval(callback, delay, ...args) 은 호스트 환경이 제공하는 반복 타이머 API. 매 delay 밀리초마다 callback 을 매크로태스크 큐에 등록한다.
전체 메커니즘은 이벤트 루프 와 비동기와 타이밍, 콜백부터 async/await까지의 발전사 참고.
사용 상황
| 상황 | setInterval | 대안 |
|---|---|---|
| 고정 간격 UI 업데이트 (시계 등) | ✅ | - |
| WebSocket heartbeat / keep-alive | ✅ | - |
| 애니메이션 프레임 갱신 | ❌ | requestAnimationFrame |
| 비동기 작업 반복 (fetch polling) | 주의 | setTimeout 재호출 패턴 |
| 단발 지연 실행 | ❌ | setTimeout |
이벤트 루프 내 동작
flowchart TD
A["setInterval(fn, 1000)"] --> B[타이머 등록]
B --> C["1000ms 경과"]
C --> D["fn 을 매크로태스크 큐에 추가"]
D --> E{콜 스택 비어있음?}
E -->|아님| F[대기]
F --> E
E -->|비어있음| G["fn() 실행"]
G --> H{clearInterval 호출?}
H -->|아님| C
H -->|호출됨| I[반복 종료]
delay 는 최소 간격. 콜 스택이 바쁘면 큐에서 대기하고 지연 실행된다.
문법
const id = setInterval(callback, delay, arg1, arg2, ...);
clearInterval(id); // 중지
| 인자 | 의미 |
|---|---|
callback | 매 주기마다 호출될 함수 |
delay | 최소 반복 간격 (밀리초). 기본 0 (브라우저는 보통 4~10ms 로 강제) |
arg1, arg2, ... | callback 에 전달할 추가 인자 |
clearInterval(id) 로 중지.
기본 사용
let count = 0;
const id = setInterval(() => {
console.log(`tick ${++count}`);
if (count === 3) clearInterval(id);
}, 1000);
// 1초마다 tick 1, tick 2, tick 3 출력 후 중지
핵심 함정
1. 콜백 실행 시간이 interval 보다 길어지면 큐가 쌓인다
가장 흔한 버그.
setInterval(() => {
// 1.5초 걸리는 작업
const start = Date.now();
while (Date.now() - start < 1500) {}
console.log('done');
}, 1000);
// 1초마다 큐에 등록되는데 한 번 실행에 1.5초
// 큐가 무한히 쌓이거나, 브라우저가 자동으로 누락
CAUTION
비동기 작업 (예: setInterval(async () => await fetch(...), 1000)) 도 같은 문제. 응답이 1초보다 늦으면 요청이 중첩된다.
2. 정확한 주기가 보장되지 않는다
setTimeout 과 마찬가지로 최소 간격. 콜 스택이 비지 않으면 늦게 실행되고, 늦은 만큼 다음 호출이 당겨지지 않는다 (만회하지 않음).
3. 백그라운드 탭에서 throttle
브라우저는 백그라운드 탭의 setInterval 을 1Hz 로 강제 (Chrome, Firefox). 시계 같은 UI 는 다른 방법 필요.
4. React useEffect 에서 cleanup 누락
// 잘못된 예: interval 이 누적됨
useEffect(() => {
setInterval(() => setCount(c => c + 1), 1000);
}, []);
// 올바른 예
useEffect(() => {
const id = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(id); // cleanup
}, []);
WARNING
React 의 StrictMode 는 개발 환경에서 Effect 를 두 번 실행한다. cleanup 이 없으면 interval 이 두 개 등록된다.
5. this 바인딩 문제
class Timer {
constructor() {
this.count = 0;
}
start() {
// 잘못: this 가 undefined (strict) 또는 window
setInterval(function() {
console.log(this.count);
}, 1000);
// 올바름: arrow function 은 외부 this 캡처
setInterval(() => {
console.log(this.count);
}, 1000);
}
}
더 안전한 대안, setTimeout 재호출
콜백 실행이 끝난 뒤 다음 호출을 예약하면 적체가 없다.
function poll() {
doWork();
setTimeout(poll, 1000); // 작업 끝난 뒤 1초
}
poll();
또는 async/await 와 함께:
async function poll() {
while (true) {
await doWork();
await new Promise(r => setTimeout(r, 1000));
}
}
poll();
TIP
실무에서 setInterval 보다 setTimeout 재호출 패턴이 안전. 비동기 작업이나 가변 길이 작업을 반복할 때 특히.
requestAnimationFrame 과의 비교 (애니메이션)
화면을 매 프레임마다 갱신해야 한다면 setInterval 대신 requestAnimationFrame 사용.
| 항목 | setInterval(fn, 16) | requestAnimationFrame(fn) |
|---|---|---|
| 호출 시점 | 16ms 마다 (큐가 비어있을 때) | 다음 paint 직전 |
| 백그라운드 탭 | 1Hz 로 throttle | 호출 안 됨 (배터리 절약) |
| 모니터 주사율 | 무시 | 적응 (60Hz, 120Hz 등) |
| 프레임 누락 | 자주 | 드묾 |
clearInterval
const id = setInterval(tick, 1000);
clearInterval(id);
// 다음 tick 부터 실행 안 됨
이미 큐에 들어간 콜백은 취소되지 않는다.
Node.js 에서의 차이
Node.js 에서는 브라우저보다 delay 가 정확하고 최소값 강제가 없다. 그러나 블로킹 코드가 있으면 마찬가지로 delay 가 발생한다.
// Node.js 전용: setInterval 반환값이 Timeout 객체
const timer = setInterval(fn, 1000);
timer.unref(); // 이 타이머만 남았을 때 프로세스 종료 허용
timer.ref(); // 다시 프로세스 유지
자주 쓰이는 패턴
타이머 / 카운트다운
function countdown(seconds, onTick, onDone) {
let remaining = seconds;
const id = setInterval(() => {
onTick(remaining);
if (--remaining < 0) {
clearInterval(id);
onDone();
}
}, 1000);
return id;
}
Heartbeat / Keep-alive
WebSocket 같은 연결에서 ping 전송.
const id = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) ws.send('ping');
}, 30000);
AbortController 연계 (취소 가능 폴링)
function startPolling(url, interval, signal) {
const id = setInterval(async () => {
if (signal.aborted) {
clearInterval(id);
return;
}
const res = await fetch(url, { signal });
console.log(await res.json());
}, interval);
signal.addEventListener('abort', () => clearInterval(id));
return id;
}
const controller = new AbortController();
startPolling('/api/status', 5000, controller.signal);
// 취소
controller.abort();
관련 위키
이 글의 용어 (5개)
- [Javascript] 이벤트 루프javascript
- 정의 이벤트 루프 (Event Loop) 는 싱글 스레드 JavaScript 엔진이 비동기 작업을 처리하기 위해 돌리는 무한 루프다. 콜 스택이 비어있을 때마다 마이크로태스크 큐…
- [JavaScript] AbortControllerjavascript
- 정의 는 비동기 작업 (fetch, event listener, 커스텀 Promise 등) 을 취소 하기 위한 표준 Web API 인터페이스. | 인터페이스 | 역할 | |:--…
- [Javascript] Microtask Queuejavascript
- 정의 Microtask Queue (마이크로태스크 큐) 는 이벤트 루프 가 매 틱마다 완전히 비울 때까지 처리하는 우선순위 큐다. 매크로태스크 큐보다 항상 먼저 처리된다. ECM…
- [JavaScript] requestAnimationFrame: 브라우저 애니메이션 루프javascript
- 정의 (rAF) 은 브라우저에게 다음 리페인트 직전에 콜백을 실행해달라고 요청합니다. 60Hz 모니터 기준 약 16.67ms 주기. 브라우저 렌더 파이프라인 브라우저는 한 프레임…
- [Javascript] setTimeoutjavascript
- 정의 은 호스트 환경 (브라우저 또는 Node.js) 이 제공하는 타이머 API. 지정한 밀리초가 지난 뒤 을 의 매크로태스크 큐에 등록한다. 자세한 흐름과 함정은 글 참고. 이…
💬 댓글