불필요한 페인트 피하기 - 애니메이션 GIF 에디션

특히 모바일에서 부드러운 프레임 속도를 얻기 위해서는 페인트를 피하는 것이 중요합니다. 그러나 가끔 가장 특이한 위치에서 페인트가 자라나기도 합니다. 이 도움말에서는 애니메이션 GIF로 인해 불필요한 페인트가 발생하는 이유와 적용할 수 있는 매우 간단한 해결 방법을 살펴봅니다.

사랑을 담은 레이어

아시다시피 최신 브라우저는 DOM 요소 그룹을 레이어라고 하는 별도의 "이미지"로 그릴 수 있습니다. 전체 페이지에 하나의 레이어가 있는 경우도 있고, 수백 또는 드물게 수천 개의 레이어가 있을 수도 있습니다.

DOM 요소가 레이어로 그룹화되고 요소 중 하나가 시각적으로 변경되면, 변경된 요소뿐 아니라 변경된 요소와 겹치는 레이어의 다른 모든 요소도 페인트해야 합니다. 다른 대상 위에 페인팅하면 덮어쓴 픽셀이 사실상 영원히 '손실'됩니다. 원래 픽셀을 다시 표시하려면 다시 페인트해야 합니다.

따라서 한 요소를 다른 요소에서 분리하여 페인트될 때 변경되지 않은 다른 요소를 다시 그릴 필요가 없도록 하고 싶을 때가 있습니다. 예를 들어 고정된 페이지 헤더를 스크롤 가능한 콘텐츠와 결합하면 콘텐츠가 스크롤될 때마다 헤더와 새로 표시되는 콘텐츠를 다시 그려야 합니다. 헤더를 별도의 레이어에 배치하면 브라우저에서 스크롤을 최적화할 수 있습니다. 스크롤하면 브라우저에서 GPU의 도움을 받아 레이어를 이동할 수 있으며 두 레이어 중 하나가 다시 페인트되는 것을 방지할 수 있습니다.

레이어를 추가할 때마다 메모리 소비가 증가하고 성능 오버헤드가 증가하므로 우수한 성능을 유지하면서 페이지를 가능한 한 적은 레이어로 그룹화하는 것이 목표입니다.

이 모든 것이 애니메이션 GIF와 어떤 관계가 있나요?

이 그림을 한 번 살펴보겠습니다.

4개의 레이어로 나누어진 웹 앱
그림 1: 4개의 레이어로 구성된 웹 앱

이는 간단한 앱에서 사용할 수 있는 레이어 설정입니다. 여기에는 4개의 레이어가 있습니다. 그중 3개 (레이어 2~4)는 인터페이스 요소입니다. 후면 레이어는 애니메이션 GIF인 로더입니다. 일반적인 흐름에서 앱이 로드되는 동안 로더 (레이어 1)를 표시한 다음 모든 것이 완료되면 다른 레이어를 표시합니다. 하지만 중요한 점은 애니메이션 GIF를 숨겨야 한다는 것입니다.

그런데 왜 숨겨야 하죠?!

좋은 질문입니다. 완벽한 세계에서는 브라우저가 GIF의 표시 여부만 확인하여 자동으로 페인트를 피할 수 있습니다. 안타깝게도 애니메이션 GIF가 화면에서 가려지거나 보이는지 확인하는 것은 일반적으로 단순히 색을 칠하는 것보다 비용이 더 많이 들기 때문에 색이 쓰이기도 합니다.

최적의 경우 GIF는 자체 레이어에 있으며 브라우저에서는 GIF를 페인트하여 GPU로 업로드하기만 하면 됩니다. 그러나 최악의 경우 모든 요소가 단일 레이어로 그룹화될 수 있으며 브라우저에서 모든 단일 요소를 다시 페인트해야 할 수 있습니다. 작업이 완료된 후에도 모든 것을 GPU에 업로드해야 합니다. 이 모든 작업은 사용자가 GIF를 볼 수 없는 경우에도 모든 GIF 프레임에 적용됩니다.

데스크톱에서는 CPU와 GPU가 더 강력하며 둘 사이에 데이터를 전송할 대역폭이 충분하기 때문에 이런 종류의 페인팅 동작에서 벗어날 수 있습니다. 하지만 모바일에서는 페인트 작업이 매우 비싸므로 각별히 주의해야 합니다.

어떤 브라우저에 영향을 미치나요?

그렇듯이, 동작은 브라우저마다 다릅니다. 현재 Chrome, Safari, Opera는 GIF가 가려진 경우에도 모두 다시 페인트를 사용합니다. 반면 Firefox는 GIF가 가려져서 다시 그릴 필요가 없다는 것을 파악합니다. Internet Explorer는 여전히 블랙박스에 불과하며, IE11에서도 F12 툴이 아직 개발 중이므로 리페인팅이 진행되고 있는지에 대한 표시가 없습니다.

이 문제가 있는지 어떻게 알 수 있나요?

가장 쉬운 방법은 Chrome DevTools에서 '페인트 직사각형 표시'를 사용하는 것입니다. DevTools를 로드하고 오른쪽 하단 모서리에 있는 톱니바퀴 (톱니 아이콘)를 누른 후 렌더링 섹션에서 페인트 직사각형 표시를 선택합니다.

Chrome DevTools 내에서 페인트 직사각형 표시 사용 설정
그림 2: Chrome DevTools에서 페인트 사각형 표시 사용 설정하기

이제 다음과 같은 빨간색 직사각형을 찾기만 하면 됩니다.

DevTools의 Show Paint Rectangles는 빨간색 직사각형으로 애니메이션 GIF 문제를 표시합니다.
그림 3: 빨간색 직사각형으로 애니메이션 GIF 문제를 보여주는 DevTools의 Show Paint Rectangles

화면의 작은 빨간색 상자는 Chrome에서 무언가를 다시 페인팅하고 있음을 보여줍니다. 다른 요소 뒤에 로더 GIF가 숨겨진 것을 알고 있으므로 이와 같은 빨간색 상자가 표시되면 표시되는 요소를 숨기고 애니메이션 GIF가 사라지는지 확인해야 합니다. 그런 경우 CSS 또는 JavaScript를 팝하여 display: none 또는 visibility: hidden를 상위 요소에 적용해야 합니다. 물론 단순히 배경 이미지일 경우 이를 삭제해야 합니다.

실제 사이트에서 이러한 행동의 예를 살펴보려면 Allegro에서 각 제품의 이미지에 명시적으로 숨겨지지 않고 가려진 로더 GIF가 포함되어 있습니다.

결론

60fps를 달성한다는 것은 페이지를 렌더링하는 데 필요한 것 하고 그 이상은 하지 않는다는 것을 의미합니다. 과도한 페인트를 제거하는 것은 이 목표를 달성하는 데 있어 중요한 단계입니다. 실행 중인 애니메이션 GIF는 불필요한 페인트를 트리거할 수 있습니다. 이러한 페인트는 DevTools의 Show paint options(페인트 직사각형 표시) 도구로 쉽게 찾아서 디버그할 수 있습니다.

애니메이션 고양이 로더 GIF가 계속 실행되는 게 아니잖아요?