JavaScript setTimeout과 setInterval 사용하기

예제 기준 · JavaScript : 브라우저 기본 Window 타이머 API (별도 라이브러리 없음)

일정 시간 뒤 한 번 실행할 때는 setTimeout(), 일정 간격으로 반복할 때는 setInterval()을 사용합니다. 예약과 취소 방법, 실행 횟수 관리, 반복 방식의 차이를 살펴봅니다.

각 절의 코드는 해당 예제의 HTML이 준비된 뒤 독립적으로 사용합니다. 실행 예제는 script를 defer로 로드하며, ‘시작’은 #start, ‘중지 / 취소’는 #stop, ‘초기화’는 #reset 버튼입니다. 횟수는 pre#result, 안내는 #statusMessage에 표시합니다. 본문에서는 안내 문구와 사용하지 않는 공통 변수를 생략했습니다.

setTimeout() 사용하기

setTimeout은 callback을 지정한 delay 이후 한 번 실행하도록 예약합니다. delay 단위는 millisecond(ms)이며 1000ms는 1초입니다. 호출은 예약 후 바로 반환하므로 다음 코드가 타이머 완료를 기다리지는 않습니다.

브라우저에서는 반환된 숫자 timer ID를 보관했다가 clearTimeout에 전달해 아직 실행되지 않은 예약을 취소할 수 있습니다. 다음 코드는 실제 예제와 같이 1000ms 뒤 횟수를 1 증가시킵니다.

let timeoutId = null;
let timeoutCount = 0;

function display() {
    document.getElementById('result').textContent = 'timeout 횟수: ' + timeoutCount;
}

function stop() {
    clearTimeout(timeoutId);
    timeoutId = null;
    document.getElementById('start').disabled = false;
}

document.getElementById('start').addEventListener('click', function () {
    stop();
    document.getElementById('start').disabled = true;
    timeoutId = setTimeout(function () {
        timeoutId = null;
        timeoutCount += 1;
        display();
        document.getElementById('start').disabled = false;
    }, 1000);
});

document.getElementById('stop').addEventListener('click', stop);
document.getElementById('reset').addEventListener('click', function () {
    stop();
    timeoutCount = 0;
    display();
});
window.addEventListener('pagehide', stop);
display();

초기 횟수는 0입니다. ‘시작’을 누르면 예약 중에는 시작 버튼을 비활성화하고, callback이 실행되면 횟수가 1이 되면서 다시 활성화합니다. 완료 후 다시 시작하면 횟수는 2로 누적됩니다. 예약이 실행되기 전에 ‘중지 / 취소’를 누르면 횟수가 늘지 않습니다.

clearTimeout은 이미 실행한 작업을 되돌리지 않습니다. ID 변수에 null을 넣는 것만으로는 취소되지 않으므로, 먼저 clearTimeout을 호출한 뒤 변수를 비웁니다. ‘초기화’는 예약을 취소하고 횟수도 0으로 만듭니다.

setInterval() 사용하기

setInterval은 지정한 간격을 기준으로 callback 실행을 반복 예약합니다. 첫 callback도 호출 즉시 실행되는 것이 아니라 간격을 기다린 뒤 실행 대상이 됩니다. 반환된 timer ID를 clearInterval에 전달하면 반복을 중지합니다.

let intervalId = null;
let intervalCount = 0;

function display() {
    document.getElementById('result').textContent = 'interval 횟수: ' + intervalCount;
}

function stop() {
    clearInterval(intervalId);
    intervalId = null;
    document.getElementById('start').disabled = false;
}

document.getElementById('start').addEventListener('click', function () {
    stop();
    document.getElementById('start').disabled = true;
    intervalId = setInterval(function () {
        intervalCount += 1;
        display();
    }, 1000);
});

document.getElementById('stop').addEventListener('click', stop);
document.getElementById('reset').addEventListener('click', function () {
    stop();
    intervalCount = 0;
    display();
});
window.addEventListener('pagehide', stop);
display();

‘시작’을 누르면 interval 횟수가 callback 실행마다 1씩 증가합니다. 반복 중에는 시작 버튼이 비활성화되어 중복 타이머 생성을 막습니다. ‘중지 / 취소’는 현재 횟수를 유지하며, 다시 시작하면 그 횟수에 이어서 증가합니다. ‘초기화’만 반복을 멈추고 0으로 되돌립니다.

setTimeout과 setInterval 차이

다음 비교 예제는 한 번의 ‘시작’으로 timeout과 interval을 모두 1000ms로 예약합니다. timeout이 실행되어도 interval은 계속됩니다. 앞의 개별 예제와 구분하여 다음 코드를 독립적으로 사용합니다.

let timeoutId = null;
let intervalId = null;
let timeoutCount = 0;
let intervalCount = 0;

function display() {
    document.getElementById('result').textContent =
        'timeout 횟수: ' + timeoutCount + '\ninterval 횟수: ' + intervalCount;
}

function stop() {
    clearTimeout(timeoutId);
    clearInterval(intervalId);
    timeoutId = null;
    intervalId = null;
    document.getElementById('start').disabled = false;
}

document.getElementById('start').addEventListener('click', function () {
    stop();
    document.getElementById('start').disabled = true;
    timeoutId = setTimeout(function () {
        timeoutId = null;
        timeoutCount += 1;
        display();
    }, 1000);
    intervalId = setInterval(function () {
        intervalCount += 1;
        display();
    }, 1000);
});

document.getElementById('stop').addEventListener('click', stop);
document.getElementById('reset').addEventListener('click', function () {
    stop();
    timeoutCount = 0;
    intervalCount = 0;
    display();
});
window.addEventListener('pagehide', stop);
display();

초기화 직후 시작하면 timeout 횟수는 한 번만 증가하고, interval 횟수는 계속 증가합니다.

조작 timeout interval
시작 한 번 실행 예약 반복 예약
중지 / 취소 실행 전 예약 취소, 횟수 유지 반복 중지, 횟수 유지
다시 시작 새로 한 번 예약, 횟수 누적 새 반복 예약, 횟수 누적
초기화 취소 후 횟수 0 중지 후 횟수 0

비교 화면에서는 timeout이 끝나도 시작 버튼을 다시 활성화하지 않습니다. interval이 남아 있으므로 중지 또는 초기화를 눌러야 합니다. 안내에도 ‘timeout이 한 번 실행되었습니다. interval은 계속됩니다.’라고 표시합니다. 같은 delay를 사용해도 두 callback이 정확히 같은 시점에 실행된다고 보장하지 않습니다.

반복 작업을 setTimeout으로 구현하기

callback 내부에서 다음 setTimeout을 예약하면 한 번 실행을 이어 붙여 반복할 수 있습니다. 다음은 실행 화면에 없는 보충 코드입니다. DOM 없이 콘솔에서 사용하며 첫 실행도 1000ms 뒤에 예약합니다.

let timerId = null;
let count = 0;

function repeat() {
    timerId = null;
    count += 1;
    console.log('실행 횟수: ' + count);

    // 위의 동기 작업을 마친 뒤 다음 실행을 예약합니다.
    timerId = setTimeout(repeat, 1000);
}

function start() {
    if (timerId !== null) {
        return;
    }
    timerId = setTimeout(repeat, 1000);
}

function stop() {
    clearTimeout(timerId);
    timerId = null;
}

window.addEventListener('pagehide', stop);
start();
// 반복을 끝내려면 나중에 stop()을 호출합니다.

이 코드는 count 증가와 콘솔 출력이 끝난 뒤 다음 대기를 시작합니다. setInterval이 간격을 기준으로 반복하는 것과 달리, 작업 후에 다음 예약을 결정할 수 있습니다. repeat를 즉시 다시 호출하는 방식이 아니라 타이머에 함수 자체를 전달합니다.

같은 메인 스레드에서 동기 callback 두 개가 동시에 실행되지는 않습니다. 다만 긴 동기 작업은 다음 callback 실행을 지연시키고, interval callback이 네트워크 요청 같은 비동기 작업을 시작하면 이전 요청이 끝나기 전에 다음 요청을 시작할 수 있습니다. 요청이 겹치거나 응답 순서가 달라질 수 있는 이유입니다.

위 반복 코드를 비동기 작업에 적용할 때는 요청을 시작한 직후가 아니라 Promise의 완료·오류 처리 이후에 다음 setTimeout을 예약해야 합니다. 중지한 동안 진행 중이던 작업이 끝나더라도 다시 예약하지 않도록 실행 여부도 확인해야 합니다. setTimeout으로 바꾸기만 하면 비동기 완료를 자동으로 기다리는 것은 아닙니다.

시간 값과 실제 실행 시점

delay는 callback 실행 전에 기다리도록 요청하는 최소 대기 시간이며 정확한 실행 시각을 보장하지 않습니다. 시간이 지나도 현재 실행 중인 JavaScript 작업이 끝나야 callback을 처리할 수 있고, 이벤트 루프 상황이나 비활성 탭의 타이머 제한에 따라 더 늦어질 수 있습니다.

따라서 예제의 ‘횟수’는 실제 callback 실행 횟수입니다. 1000ms를 지정했다고 해서 그 횟수가 정확한 경과 초를 뜻하지는 않습니다. callback에는 문자열 코드 대신 함수를 전달하고, delay도 숫자로 지정합니다.

더 이상 필요하지 않은 타이머는 정리합니다. 세 실행 예제는 pagehide에서 stop을 호출합니다. 페이지 이동뿐 아니라 컴포넌트가 제거되거나 화면의 반복 작업이 필요 없어지는 시점에도 저장한 ID로 해제하는 습관이 중요합니다. clearTimeout과 clearInterval은 이미 시작된 callback이나 그 안에서 시작한 네트워크 요청까지 취소하지는 않습니다.

정리

API 역할 사용 상황
setTimeout() delay 이후 한 번 실행 예약 지연 처리, 완료 후 다음 실행 예약
clearTimeout() timer ID로 예약 취소 불필요해진 지연 작업 정리
setInterval() 간격을 기준으로 반복 예약 간단한 주기적 상태 갱신
clearInterval() timer ID로 반복 중지 중지 버튼, 화면 종료 시 정리

참고자료