setInterval 대신 requestAnimationFrame으로 사용합시다!!

쭌로그·2025년 6월 22일
4

웹 페이지의 애니메이션을 구현할 때, CSS의 transform, animation, transition과 같은 속성들로 조작할 수 있지만, 사용자와의 인터렉션을 통한 복잡한 animation을 구현할 때는 javascript을 사용하여 애니메이션을 구현합니다. 예를 들어 버튼을 클릭하면 바운스 뒤 사리지는 효과, 스크롤 시 발생하는 다양한 애니메이션들이 여기에 해당합니다.

그렇기 때문에 CSS로 조작이 어려운 애니메이션의 경우 javascript로 구현하는 경우가 많은데 javascript로 구현하는 경우에는 CSS로 구현할 때보다 성능면에서 떨어지게됩니다. 이러한 성능을 개선하기 위해 여러 최적화 기법이 존재하는데 오늘은 requestAnimationFrame이란 메서드를 통해 최적화하는 방법을 소개하려고합니다.

requestAnimationFrame이란?

window.requestAnimationFrame() 메서드는 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트 바로 전에 브라우저가 애니메이션을 업데이트할 지정된 함수를 호출하도록 요청합니다. 이 메서드는 리페인트 이전에 호출할 인수로 콜백을 받습니다.

위 설명은 MDN 공식 사이트에 기재되어 있는 requestAnimationFrame 설명입니다. 위 내용에서 리페인트 바로 전에 호출하도록 요청한다고 하는데 이를 이해하기 위해는 브라우저의 렌더링 단계를 이해해야합니다.

브라우저의 렌더링 단계

브라우저의 렌더링 단계는 총 5단계로 나뉩니다.

  1. DOM 트리 생성
  2. CSS 돔 트리 생성
  3. Render Tree 생성
  4. layout(Reflow)
  5. paint(Repaint)
  6. Composite

브라우저의 렌더링 단계는 크게 위 6단계를 따르고 있습니다. requestAnimationFrame은 위 단계 중 repaint 단계에 맞춰 애니메이션을 실행시켜줍니다.

일반적으로 60FPS에 맞춰 실행되지만, 사용자의 디스플레이 주사율(hz)에 따라 변경될 수 있습니다.

60FPS에 맞춰 실행된다는 의미는 1초에 60개의 프레임을 만들기 위해 1000ms/60FPS = 16.6ms 마다 콜백 함수가 호출된다는 의미입니다.

용어 정리

  • fps(frame per second) : 그리고 특정 시간 내에 보여지는 frame 갯수
  • hz(Hertz) : 1초동안 진동하는 수. 화면이 1초동안 새로고침 하는 횟수.

왜 60hz가 기본 단위일까?

사람의 눈은 1초에 60번 이상의 장면이 넘어가야 부드럽다고 느낀다고합니다. 때문에 현대 영상 및 애니메이션은 최소 초당 60번 화면을 그리도록 설계합니다.

프레임 설명

타이머 메서드와 requestAnimationFrame의 차이점

앞서 말했듯이 requestAnimationFrame은 특정 주기에 맞춰 코드를 재호출하는 방식으로 동작합니다. 그렇다면 setInterval, setTimeout과 같은 메서드로 구현할 수도 있는데 왜 requestAnimationFrame을 사용하는 것일까요?

function someFunction(){}

setInterval(someFunction, 1000/60);
setTimeout(someFunction, 1000/60);

Timer 메서드의 문제점

그 이유는 "requestAnimationFrame은 위 단계 중 repaint 단계에 맞춰 애니메이션을 실행시켜줍니다."라는 설명과 연관되어 있습니다. setInterval과 setTimeout과 같은 타이머 메서드는 단순히 특정 시간, 특정 주기에 맞춰 메서드를 실행할 뿐, 프레임을 신경쓰지 않고 동작합니다.

만약 특정 주기마다 실행되어야하는 메서드가 다른 작업으로 인해 지연되어 프레임 중간에 실행될 수 있습니다. 그렇게 된다면 특정 프레임이 생성되지 못하고 프레임이 깎이는 프레임 드랍 현상이 발생할 수 있습니다. 프레임 드랍이 발생하면 사용자는 화면이 버벅이는 듯한 움직임을 받게되고 UX적으로 안좋은 현상이 발생할 수 있게 됩니다.

requestAnimationFrame

requestAnimationFrame은 타이머 메서드와 달리 프레임을 그릴 준비가 되면 화면이 갱신되는 주기에 맞춰 메서드를 실행합니다. 이는 프레임 드랍이 발생하는걸 방지해주며 끊기는 현상 없이 부드럽게 애니메이션을 만들어줍니다.

위 사진은 메서드에 수행 시간에 관계 없이 프레임 시간에 맞춰 실행시키는 모습을 시각화한 것입니다.

requestAnimationFrame 사용 방법

requestAnimationFrame(callback);

  • callback
    다음 리페인트를 위한 애니메이션을 업데이트할 때 호출할 함수. 콜백 함수에는 requestAnimationFrame()이 콜백 함수들의 실행을 시작할 시점을 나타내는 performance.now() 에 의해 반환되는 것과 유사한 DOMHighResTimeStamp 단일 인수가 전달됩니다.

  • DOMHighResTimeStamp
    현재 프레임이 시작된 시간을 나타내는 고해상도 타임스탬프입니다. 이 값을 사용하여 애니메이션의 진행 상태를 계산하고 애니메이션 속성을 업데이트할 수 있습니다.

  function animate(timestamp) {
    if (!start) start = timestamp;
    const elapsed = timestamp - start;

    const progress = Math.min(elapsed / duration, 1);
    const x = distance * progress;

    box.style.transform = `translateX(${x}px)`;

    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }

requestAnimationFrame의 장점

  1. 백그라운드 동작 중지
    requestAnimationFrame은 페이지가 비활성화 될 때 브라우저에 의해 일시 중지되기 때문에 CPU 리소스를 절약할 수 있습니다.

  2. 주사율에 맞춘 호출 횟수
    requestAnimationFrame은 자동으로 hz에 맞춰 호출 횟수를 지정해주기 때문에 개발자가 따로 설정하지 않아도 최적화 된 호출 횟수를 제공합니다.

  3. Animation Queue 처리
    requestAnimationFrame 메서드는 타이머 이벤트와 같이 애니메이션을 그리기 위한 콜백으로 비동기로 분류되어 처리합니다. 이 때 requestAnimationFrame은 다른 비동기 작업과 달리 animation frame이라는 별개의 queue에서 처리됩니다.

정리

requestAnimationFrame은 자바스크립트로 애니메이션을 조작할 때 최적화하기 위핸 API로 화면 갱신 주기에 맞춰 프레임 드랍없이 애니메이션을 실행시켜줍니다. 또한 hz에 맞춰 메서드 호출 횟수를 정해주기 때문에 성능적으로도 최적화 된 메서드 호출 횟수를 저장해줍니다.

참조

requestAnimationFrame(rAF) 톺아보기
🌐웹 애니메이션 최적화 requestAnimationFrame 가이드
출처: https://inpa.tistory.com/entry/🌐-requestAnimationFrame-가이드#requestanimationframe_장점 [Inpa Dev 👨‍💻:티스토리]

profile
매일 발전하는 프론트엔드 개발자

0개의 댓글