RequestAnimationFrame vs SetInterval

RequestAnimationFrame
Dragon C's avatar
Apr 17, 2025
RequestAnimationFrame vs SetInterval
  • 개요

    • 한번에 돌아가는 여러개의 타이머 제어를 해야하는 상황

  • 주요 타이머 기능

    • 인증번호 전송 이후 한번은 바로 재 전송 가능 그 이후에는 1분에 한번씩 가능

    • 휴대폰 번호 변경시 초기화 → 재 전송 즉시 가능

    • 재전송 버튼 누른 이후에는 60초 카운트다운 버튼 내 표기

  • 고민

    • 최적화 문제로 setInterval과 requestAnimationFrame 사용에 대한 고민

    • 아래와 같은 이유로 최종적으로는 setInterval를 사용하였지만 requestAnimationFrame을 사용해야 하는 시나리오에 대해서도 고민을 해보았다.

  • 1. 정확한 시간 간격 유지

    • setInterval은 지정된 시간 간격마다 정확히 함수를 실행합니다. 예를 들어, 타이머가 1초마다 실행되어야 할 때, setInterval은 거의 정확하게 1초마다 실행되는 작업을 보장한다.

    • 반면, requestAnimationFrame은 브라우저의 화면 리프레시 주기에 따라 실행되므로 정확한 간격을 맞추기 어렵다.

    2. 타이머 및 주기적 작업에 적합

    • setInterval은 주기적인 작업을 반복할 때 이상적입니다. 예를 들어, 카운트다운 타이머, 반복적인 데이터 전송, 또는 특정 작업을 일정한 시간 간격으로 실행해야 할 때 매우 유용하다.

    • requestAnimationFrame은 주로 애니메이션에 적합하며, 주기적이고 정확한 시간 간격의 작업에는 적합하지 않다.

    3. 브라우저 리프레시와 독립적인 동작

    • setInterval은 브라우저의 리프레시 주기와 무관하게 작동합니다. 즉, 브라우저가 화면을 다시 그리거나 다른 애니메이션을 처리할 때에도 정확히 설정된 시간 간격에 따라 동작을 실행한다.

    • requestAnimationFrame은 화면 리프레시와 동기화되기 때문에, 화면이 리프레시되거나 애니메이션이 실행되지 않으면 실행되지 않거나 지연될 수 있다.

function isButtonValidAfterOneMinute() {
  isAuthResent.value = true;
  const interval = setInterval(() => {
    resendButtonValidTime.value--;
    if (resendButtonValidTime.value <= 0 || isAuthResent.value === false || isVerified.value === true) {
      clearInterval(interval);
      resendButtonValidTime.value = 60;
    }
  }, 1000);
  setTimeout(() => {
    isAuthResent.value = false;
    clearInterval(interval);
  }, 60000);
}
function stopTimer(timer: NodeJS.Timeout) {
  clearInterval(timer);
  countingTime.value = 0;
  countingStr.value = '00:00';
}

setInterval

  • 1초 (1000ms) 마다 함수를 실행하여 HTML 요소를 업데이트하는 방식으로 렌더링하게 되는데,

    타이머 하나를 보여주려고 백그라운드에서 계속 돌아가는 setinterval 함수를 사용하는 것은 아래와 같은 문제점이 생긴다.

1.정확한 실행 시간 보장 불가능

  • setInterval 함수는 일정한 시간 간격으로 코드를 실행하는 것이 아니라, 이벤트 루프에서 실행 대기열에 추가된 시점부터 일정 시간이 지난 후 실행됨. 따라서, 코드 실행이 지연될 가능성이 농후하다.

  1. 코드 충돌 가능성

  • setInterval 함수를 사용하면서, 두 개 이상의 코드가 동시에 실행될 가능성이 있음. 이 경우, 코드 충돌이 발생하여 예상치 못한 결과가 발생할 수 있음.

  1. 반복 시간 간격의 불일치:

setInterval 함수를 사용하여 일정 시간 간격으로 코드를 실행하면 시간 간격이 정확하게 유지되지 않을 수 있음. 이는 브라우저의 성능에 따라 달라질 수 있음.

  1. 메모리 사용:

setInterval 함수를 사용하면 반복적으로 실행되는 코드가 메모리를 계속해서 사용하게 됨. 이는 메모리 누수를 유발할 수 있음.

  1. 중복 실행:

setInterval 함수를 사용하면 이전 코드 실행이 완료되지 않은 상태에서 새로운 코드가 실행될 가능성이 있음. 이러한 경우, 코드 중복 실행이 발생할 수 있음.

requestAnimationFrame()

  • requestAnimationFrame 함수는 브라우저의 성능을 최적화하고 애니메이션을 부드럽게 처리하기 위한 자바스크립트 함수. 브라우저의 화면 갱신 주기에 맞추어 애니메이션을 처리하므로, 애니메이션의 부드러운 움직임을 보장한다.

해당 함수가 setInterval 함수보다 나은 경우

  1. 더 나은 성능

requestAnimationFrame 함수는 브라우저의 화면 갱신 주기에 맞추어 호출되기 때문에, 브라우저가 새로운 프레임을 그리기 전까지 실행되지 않는다. 이를 통해, 애니메이션이 부드럽게 처리되고 브라우저의 성능을 최적화할 수 있다.

setInterval 함수는 정해진 시간 간격으로 함수를 호출하는 방식으로 애니메이션을 처리하기 때문에 브라우저의 성능과 화면 갱신 주기와 일치하지 않을 수 있다.

  1. 메모리 절약

화면 갱신 주기에 맞추어 호출되기 때문에 불필요한 처리를 방지하고 메모리를 절약할 수 있다. 이는 모바일 기기에서 특히 중요하다. (많은 메모리의 사용은 배터리 수명과 직결되기 때문)

  1. 다른 탭과의 경쟁 회피:

requestAnimationFrame 함수는 브라우저가 비활성화되거나 다른 탭으로 전환되어 있는 경우에는 호출되지 않는다. 이는 다른 탭과의 경쟁을 회피하고 브라우저의 성능을 유지하는 데 도움된다.

  1. 동일한 타이머 공유:

여러개의 애니메이션을 생성해도, 각각의 타이머를 생성하는것이 아닌, 내부의 동일한 타이머를 참조하여 불필요한 메모리를 사용하지 않는다.

requestAnimationFrame 함수 예시

let now = new Date();
let after1sec = new Date(now.getTime() + 1000);
function printTime() {
	let now = new Date();
	if (after1sec.getTime() - now.getTime() > 0) {
		console.log('record!');
		printTime();
	}
}
printTime();
  • 지나치게 많은 콜스택이 쌓여 에러가 발생한다.

requestAnimationFrame을 이용하여 똑같이 1초동안 콘솔을 찍어보면 어떤 결과?

let startTime = null;
function printTime(timestamp) {
	if (!startTime) startTime = timestamp;
	console.log('record!');
	if (timestamp - startTime < 1000) {
		requestAnimationFrame(printTime);
	}
}
requestAnimationFrame(printTime);

위의 코드처럼 무제한 호출되는 것이 아닌, 1초에 60번의 호출만 발생한다.

즉, 1/60으로 애니메이션이 제한되어 최적화된 속도로 부드러운 애니메이션을 표현하면서 성능은 최대한 확보할 수 있게 된다.

cancelAnimationFrame()

setInterval 함수가 clearInterval 함수로 끝낼 수 있다면, requestAnimationFrame 함수는 cancelAnimationFrame 함수로 종료시킬 수 있다.

let requestId;
function draw() {
	// 애니메이션 그리기
	requestId = requestAnimationFrame(draw);
}
// 애니메이션 시작
requestId = requestAnimationFrame(draw);
// 5초 후 애니메이션 중지
setTimeout(() => {
	cancelAnimationFrame(requestId);
}, 5000);
  • requestAnimationFrame 함수를 변수에 할당하고, cancelAnimationFrame 함수를 실행 할 때 해당 변수를 인수로 건네주면 종료된다.

setInterval과 requestAnimationFrame의 차이점

  1. 속도 조절 가능 여부

setInterval은 속도를 조절할 수 있는 반면, requestAnimationFrame은 속도 조절이 불가하다.

무조건 1초에 60번 실행된다. (≒0.0167초에 한 번 실행)

  • 이유는 일반적인 모니터의 화면 재생률이 60Hz이기 때문이다.

 

  1. 콜백 실행 방식

requestAnimationFrame에서는 다음 콜백을 실행하려면 반드시 콜백 함수 내에서 requestAnimationFrame을 재호출해야만 한다.

반면에 setInterval의 경우에는 clearInterval로 중지하지 않는 한 영원히 실행된다. 만약 애니메이션을 중지하고 싶다면, 해당 함수를 갖는 변수를 별도로 선언한 후, clearInterval 함수에 해당 변수를 파라미터로 전달하여 중지시켜야 한다.

만약 setInterval로 만든 애니메이션을 중지하지 않고 또 애니메이션 함수를 호출하는 경우에는 아래와 같이 애니메이션이 겹쳐져서 버벅임이 발생한다.

  • 버벅이는 현상

  • 의도했던 애니메이션

  1. 프레임의 부드러움

setInterval으로 구현한 애니메이션은 약간의 프레임 끊김이 발생하거나 프레임 자체를 빠뜨리는 문제가 발생할 수 있다.

requestAnimationFrame은 애니메이션을 위해 최적화된 함수이므로 애니메이션이 실행되는 환경에 관계 없이 적절한 프레임 속도로 실행되며, 탭이 활성화되지 않은 상태이거나 애니메이션이 페이지를 벗어난 경우에도 계속 실행되는 기존의 문제점을 해결할 수 있는 방법이다.

 

  1. IE에서의 지원 여부

requestAnimationFrame은 IE 10버전부터 지원한다.

따라서 IE 10버전 이하 브라우저도 지원하려면 setInterval을 사용하거나 polyfill로 requestAnimationFrame을 주입해야 한다.

해당 상황에서 requestAnimationFrame을 도입할만한 상황

  • 타이머가 진행됨에 따라 버튼의 상태가 계속해서 변경되어야 한다면, requestAnimationFrame은 브라우저의 리프레시 주기에 맞춰 빠르고 효율적으로 UI 업데이트를 처리할

  • 사용자가 재전송 버튼을 누른 후, 버튼 상태를 "재전송 가능"으로 바로 바꾸거나 애니메이션 효과를 추가해야 할 때

  • 타이머가 끝날 때 버튼에 애니메이션 효과를 주거나, 중간에 변경되는 Input의 상태를 부드럽게 전환하고 싶을 때

Share article

cmun2