자바스크립트 실행 최적화

JavaScript는 종종 시각적 변화를 유발합니다. 어떤 경우에는 스타일 조작을 통해 직접 구현되거나, 어떤 경우에는 데이터 검색 또는 정렬과 같은 시각적 변화를 일으키는 계산을 통해 구현됩니다. 타이밍이 나쁘거나 실행 시간이 긴 자바스크립트는 성능 문제의 일반적인 원인입니다. 가급적 이러한 영향을 최소화할 수 있는 방법을 찾아야 합니다.

폴 루이스
폴 루이스

JavaScript는 종종 시각적 변화를 유발합니다. 어떤 경우에는 스타일 조작을 통해 직접 구현되거나, 어떤 경우에는 데이터 검색 또는 정렬과 같은 시각적 변화를 일으키는 계산을 통해 구현됩니다. 타이밍이 나쁘거나 실행 시간이 긴 자바스크립트는 성능 문제의 일반적인 원인입니다. 가급적 이러한 영향을 최소화할 수 있는 방법을 찾아야 합니다.

작성하는 JavaScript가 실제로 실행되는 코드와 다르기 때문에 JavaScript 성능 프로파일링은 예술적일 수 있습니다. 최신 브라우저는 JIT 컴파일러와 모든 방식의 최적화 및 유용한 정보를 사용하여 가능한 한 가장 빠른 실행을 제공하며 이로 인해 코드의 작동 방식이 크게 달라집니다.

그래도 앱이 자바스크립트를 잘 실행하도록 확실히 할 수 있는 몇 가지 방법이 있습니다.

요약

  • 시각적 업데이트에 setTimeout 또는 setInterval을 피하고 대신 항상 requestAnimationFrame을 사용합니다.
  • 기본 스레드를 벗어나 오래 실행되는 자바스크립트를 Web Workers로 이전합니다.
  • 마이크로 작업을 사용하여 여러 프레임에서 DOM을 변경합니다.
  • Chrome DevTools의 타임라인 및 자바스크립트 프로파일러를 사용하여 자바스크립트의 영향을 평가합니다.

시각적 변경사항에 requestAnimationFrame 사용

화면에서 시각적 변경이 발생하면 프레임 시작 시점인 브라우저에서 적절한 시점에 작업을 실행하려고 합니다. 자바스크립트가 프레임 시작 시 실행되도록 보장하는 유일한 방법은 requestAnimationFrame를 사용하는 것입니다.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

프레임워크나 샘플에서는 setTimeout 또는 setInterval를 사용하여 애니메이션과 같은 시각적 변경을 실행할 수 있지만, 문제는 콜백이 프레임의 특정 지점(아마도 마지막)에서 실행되어 종종 프레임을 놓치고 버벅거림이 발생하는 효과를 일으킬 수 있다는 점입니다.

setTimeout이 실패하여 브라우저에서 프레임이 누락됩니다.

실제로 jQuery는 animate 동작에 setTimeout를 사용했습니다. 버전 3에서 requestAnimationFrame를 사용하도록 변경되었습니다. 이전 버전의 jQuery를 사용하는 경우 requestAnimationFrame를 사용하도록 패치하는 것이 좋습니다.

복잡성 감소 또는 Web Workers 사용

자바스크립트는 브라우저의 기본 스레드에서 스타일 계산, 레이아웃 및 대부분의 경우 페인트와 함께 실행됩니다. 자바스크립트가 오랫동안 실행되면 이러한 다른 작업이 차단되어 프레임이 누락될 수 있습니다.

자바스크립트를 언제 얼마나 오래 실행할지 전략을 세워야 합니다. 예를 들어 스크롤과 같은 애니메이션의 경우 자바스크립트를 3~4ms의 영역으로 유지하는 것이 이상적입니다. 그보다 오래 걸리면 너무 많은 시간이 걸릴 위험이 있습니다. 유휴 기간인 경우 소요 시간에 대한 걱정을 덜 수 있습니다.

예를 들어 DOM 액세스가 필요하지 않은 경우 등 많은 경우 순수한 계산 작업을 웹 작업자로 이전할 수 있습니다. 정렬이나 검색과 같은 데이터 조작 또는 순회는 로드 및 모델 생성과 마찬가지로 이 모델에 잘 맞는 경우가 많습니다.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

모든 작업이 이 모델에 적합한 것은 아닙니다. 웹 작업자는 DOM에 액세스할 수 없습니다. 작업이 기본 스레드에 있어야 하는 경우 더 큰 작업을 각각 몇 밀리초 이하의 마이크로 작업으로 세분화하고 각 프레임의 requestAnimationFrame 핸들러 내부에서 실행하는 일괄 처리 접근 방식을 고려하세요.

이 접근 방식에는 UX 및 UI가 있으며 진행 상황 또는 활동 표시기를 사용하여 사용자가 작업이 처리 중임을 알 수 있도록 해야 합니다. 어떤 경우든 이 접근 방식은 앱의 기본 스레드를 사용 가능한 상태로 유지하여 사용자 상호작용에 계속 반응할 수 있도록 합니다.

자바스크립트의 '프레임 비용' 알아보기

프레임워크, 라이브러리 또는 자체 코드를 평가할 때는 프레임별로 자바스크립트 코드를 실행하는 데 드는 비용을 평가하는 것이 중요합니다. 이는 전환이나 스크롤과 같이 성능이 중요한 애니메이션 작업을 실행할 때 특히 중요합니다.

Chrome DevTools의 성능 패널은 자바스크립트 비용을 측정하는 가장 좋은 방법입니다. 일반적으로 다음과 같은 하위 수준의 레코드를 얻습니다.

Chrome DevTools의 성능 기록

Main 섹션에는 JavaScript 호출에 대한 Flame Chart가 제공되므로, 호출된 함수와 각 함수에 걸린 시간을 정확하게 분석할 수 있습니다.

이 정보를 통해 자바스크립트가 애플리케이션에 미치는 성능 영향을 평가하고 함수를 실행하는 데 시간이 너무 오래 걸리는 핫스팟을 찾아 수정할 수 있습니다. 앞서 언급했듯이 장기 실행 자바스크립트를 삭제하거나, 제거가 불가능한 경우 웹 작업자로 이동하여 기본 스레드를 확보하여 다른 작업을 계속 진행해야 합니다.

성능 패널을 사용하는 방법은 런타임 성능 분석 시작하기를 참조하세요.

자바스크립트 미세 최적화 피하기

브라우저가 어떤 버전의 항목을 다른 버전보다 100배 더 빠르게 실행할 수 있다는 점도 알아두면 좋을 수 있습니다. 예를 들어 요소의 offsetTop을 요청하는 것이 getBoundingClientRect()를 계산하는 것보다 빠르지만, 이와 같은 함수는 프레임당 소수의 횟수로만 호출되므로 자바스크립트 성능 측면에 집중하는 것은 일반적으로 노력이 낭비됩니다. 일반적으로 밀리초의 일부만 절약할 수 있습니다.

게임이나 계산 비용이 많이 드는 애플리케이션을 만드는 경우에는 일반적으로 많은 계산을 단일 프레임에 적용하게 되고 이 경우에는 모든 것이 도움이 되므로 이 지침은 예외일 수 있습니다.

간단히 말해, 마이크로 최적화는 빌드 중인 애플리케이션에 일반적으로 매핑되지 않으므로 매우 조심해야 합니다.