Avoiding Unnecessary Paints

HTML5 Rocks

소개

사이트나 어플리케이션에서 엘리먼트의 그리기는 정말 큰 비용이 필요할 수도 있으며 런타임 성능에 부정적인 연쇄 효과를 일으킬 수도 있습니다. 이 글에서 브라우저에서 무엇이 페인팅을 발생할 수 있는지와 어떻게 불필요한 페인드를 방지할 수 있는지에 대해 빠르게 살펴보도록 하겠습니다.

페인팅: 아주 빠르게 둘러봅시다

브라우저가 수행하는 주요 작업 중의 하나는 DOM과 CSS를 화면 상의 픽셀로 변환하는 것이며 이를 위해 꽤 복잡한 프로세스를 거쳐야 합니다. 이 작업은 마크업을 읽는 것으로 시작하여 이를 통해 DOM 트리를 생성합니다. 이것은 마치 CSS가 CSSOM을 생성하는 것과 유사하게 동작합니다. DOM과 CSSOM은 결합된 후에 비로소 몇 픽셀을 그리기 시작하는 구조에 도달합니다.

만약 브라우저가 어떻게 동작하는지에 대한 깊이 이해하고 싶다면 여기 HTML5 Rocks의 상세한 글를 반드시 확인해보세요!

페인팅 프로세스 자체는 흥미롭습니다. 크롬에서 DOM 트리와 CSS는 Skia라고 불리는 소프트웨어에 의해 래스터라이징됩니다. 만약 canvas 엘리먼트를 사용해보셨다면 Skia의 API는 정말 친숙할 것입니다. 거기에는 많은 고급 기능들과 더불어 많은 moveTo- 와 lineTo- 스타일의 함수가 있습니다. 근본적으로 모든 엘리먼트는 실행 가능한 Skia 호출의 집합으로 정제되고 다량의 비트맵들로 이루어진 출력으로 그려져야 합니다. 이러한 비트맵은 GPU로 업로드되어 GPU가 그에 대한 합성을 통해 우리에게 화면 상의 최종 이미지를 표시합니다.

제거되어야 할 것은 엘리먼트에 적용하고자 하는 스타일에 직접 영향을 받는 Skia의 작업량입니다. 만약 알고리즘적인 무거운 스타일을 사용하면 Skia는 더 많은 작업을 실행해야 할 것입니다. 이에 대해 더 깊이 알고 싶다면 Colt McAnlis가 쓴 페이지 렌더링 부하에 CSS가 어떻게 영향을 주는가에 대한 글을 읽어보시기 바랍니다.

지금까지 말한 것을 보면, 페인트 작업은 실행을 위한 시간을 소요하며 만약 이를 줄이지 못한다면 16ms 이하의 프레임 비용을 넘어버릴 것입니다. 결국 사용자는 우리가 프레임을 놓치고 있고 멈칫거리며 보이는 것을 알아채게 되고 앱의 사용자 경험에 엄청난 상처를 줄 것입니다. 그러한 것을 정말 원하지 않는다면 어떠한 종류의 것들이 페인트 작업을 필요하게 만드는지와 우리가 무엇을 할 수 있는지에 대해 알아보도록 합시다.

스크롤(Scrolling)

브라우저에서 위아래로 스크롤될 떄마다 화면에 내용을 나타내기 전에 새로 그려야합니다. 모든 것은 작은 영역만을 차지하겠지만 그러한 경우도 역시 복잡합 스타일이 적용된 형태로 그려져야 할 수도 있습니다. 그러므로 작은 영역에 그려진다는 것이 그것이 빠르게 처리된다는 것과 동일한 의미는 아닙니다.

어떤 영역이 새로 그려지고 있는지 보기 위해 크롬 개발자 도구의 “Show Paint Rectangles” 기능을 사용할 수 있습니다. (오른쪽 아래 구석의 작은 cog 버튼을 누르면 됩니다.) 그리고 개발자 도구가 열린 뒤 그냥 페이지를 사용하여 크롬이 페이지의 일부를 언제, 어디서 그리고 있는지를 보여주는 반짝이는 사각형을 볼 수 있을 겁니다.

크롬 개발자 도구의 Show Paint Rectangles

스크롤 성능은 성공적인 사이트에 중요합니다. 사용자는 사이트나 어플리케이션의 스크롤이 잘 동작하지 않을 때를 잘 알아채고 불편해합니다. 그러므로 스크롤이 일어나는 동안 페인팅 작업을 아주 경량화하여 유지하는데 주의하고 있어야 사용자가 멈칫거리는 현상을 보지 않습니다.

여러분이 만약 스크롤 성능에 특정한 것들을 더 알고 싶으시다면 제가 예전에 쓴 스크롤 성능에 대한 글을 읽어보시기 바랍니다.

상호작용들(Interactions)

호버(hovers), 클릭(clicks), 터치(touches), 드래그(drags)와 같은 상호작용들은 또 다른 페인트 작업을 발생합니다. 사용자가 저러한 상호작용 중의 하나를 실행하면(여기서는 호버를 예로 듭니다.) 크롬은 영향받는 엘리먼트를 새로 그리려고 할 것입니다. 그리고 스크롤에 대한 것이기는 하지만 크고 복잡한 페인트는 프레임율의 저하를 수반할 것입니다.

모든 사람들이 멋지고 부드러운 상호작용을 가진 애니메이션을 원하지만 애니메이션에서 스타일이 변경되면 지나치게 많은 시간을 소요하지는 않는지에 대해 다시 확인하는 과정이 필요할 것입니다.

유감스러운 조합

고비용의 페인트를 사용하는 데모

만약 스크롤을 하고 동시에 마우스를 움직이면 무슨 일이 일어날까요? 값비싼 페인트를 호출하는 것은 기존에 스크롤했던 것처럼 엘리먼트를 사용하여 무심코 "상호작용"하는 것으로 충분히 가능합니다. 그것은 차례대로 16.7ms 이하(초당 60 프레임을 유지하기 위해 필요한 최대 시간)의 프레임 비용을 억지로 강행할 것입니다. 제가 말하고자하는 바를 정확하게 보여줄 데모를 작성했습니다. 스크롤과 마우스 이동으로 호버(hover) 효과를 표시하는 것을 볼 수 있기를 바라지만 크롬 개발자 도구에서 무엇을 만들어내는지 보도록 합시다.

크롬 개발자 도구가 고비용의 프레임을 표시

위 그림에서 블록 중의 하나에 머물 때 개발자 도구가 페인트 작업을 등록하는 것을 볼 수 있을 것입니다. 제 주장을 보여주기 위해 데모에 엄청나게 무거운 스타일 몇개를 넣어두었으므로 프레임 비용을 종종 밀어 올리고 있습니다. 마지막으로 제가 원하는 것은 이 불필요한 페인트 작업을 스크롤을 하는 동안 다른 작업들을 끝내고 처리하는 것입니다!

그럼 우리가 이것을 어떻게 멈출 수 있을까요? 공교롭게도 구현하기에 꽤 쉬운 수정 방법이 있습니다. 이 트릭은 호버(hover) 효과를 비활성화하고 다시 활성화하는 타이머를 설정하는 scroll 핸들러를 붙이는 것입니다. 이는 스크롤을 하였을 떄 그 어떤 상호작용에 대한 값비싼 그리기를 수행하지 않아도 됨을 보증하는 것을 의미합니다. 충분히 길게 스크롤이 멈추었을 때 그것들을 다시 안전하게 교환하도록 할 것입니다.

이러한 변경이 어플리케이션의 사용자 경험에 영향을 미치기 때문에 현명하게 적용해야 합니다. 여러분과 여러분의 팀만 호버(hover) 효과가 다시 활성화되기 전에 괜찮은 일정 지연수준을 호출할 수 있습니다.

여기 코드가 있습니다.

// 호버 효과의 활성화를 추적하기 위해 사용
var enableTimer = 0;

/*
 * 스크롤을 감시하고
 * 호버 효과의 사용 가능성을 제거하기 위해 사용
 */
window.addEventListener('scroll', function() {
  clearTimeout(enableTimer);
  removeHoverClass();

  // 1초 뒤에 활성화, 원하는 것을 넣으면 됩니다!
  enableTimer = setTimeout(addHoverClass, 1000);
}, false);

/**
 * 호버 클래스를 body에서 제거.
 * 호버 스타일은 이 클래스에서 표현하는 바에 의존적임.
 */
function removeHoverClass() {
  document.body.classList.remove('hover');
}

/**
 * body에 호버 클래스를 추가.
 * 호버 스타일은 이 클래스에서 표현하는 바에 의존적임.
 */
function addHoverClass() {
  document.body.classList.add('hover');
}

호버 효과의 "허가" 여부를 추적하기 위해 body에 클래스를 사용한 것과 클래스가 표현하는 것에 따른 근본적인 스타일을 볼 수 있습니다.

/*  호버 효과를 수행하기 전에 호버 클래스가 body에 있어야 함 */
.hover .block:hover {
 …
}

그리고 이게 전부입니다!

결론

렌더링 성능은 사용자가 어플리케이션을 향유하기 위해 중요하기 때문에 항상 페인트 작업량을 16ms 이하로 유지하는데 집중해야 합니다. 그것을 위해 여러분의 개발 프로세스를 전부에 병목점들을 찾고 수정할 수 있는 개발자 도구의 사용을 통합하여야 합니다.

우연한 상호작용들과 부분적으로 무겁게 그려지는 엘리먼트들은 아주 큰 비용을 소모할 수 있으며 렌더링 성능을 매우 저하시킬 것입니다. 여러분이 본 바와 같이 아주 작은 량의 코드를 통해 이를 수정할 수 있습니다.

여러분의 사이트와 어플리케이션에서 한번 확인해보세요. 이 작은 페인팅 방지 방법을 적용하고 있습니까?

Comments

0