새로운 웹페이지 성능 측정 지표 CLS(Cumulative Layout Shift)

지난 chrome 86 업데이트에서 개발자도구 – Performance 패널 – Experience 섹션에
Cumulative Layout Shift (CLS) 라는 새로운 성능 측정 지표가 생겼습니다.

CLS가 무엇인지 우리는 어떻게 대응할 수 있을지 정리해보았습니다.


I. CLS(Cumulative Layout Shift) 란?

방문자에게 콘텐츠가 얼마나‌ 불안정한 지 측정하는 사용자 경험 측정 항목 입니다.

현재 보고 있는 페이지에 갑자기 발생하는 레이아웃의 변경은 시각적으로 거슬리며 사용자의 주의를 산만하게 합니다.
또한 잘못된 클릭을 유도하여 실제 피해를 일으킬 수도 있고 아주 실망스러운 사용자 경험으로 이어질 것입니다.

CLS(Cumulative Layout Shift) 측정은 사용자에게 발생하는 레이아웃 이동(layout shift) 빈도를 측정하여 이 문제를 해결하는 데 도움이 됩니다.

CLS 점수

일정기간동안 레이아웃 이동이 없는 상태에서 발생하는 예상하지 않은 레이아웃 이동에 대한 누적된 점수입니다.
뷰포트에서 이동한 콘텐츠의 양과 영향을 받은 요소가 이동한 거리를 확인합니다.

좋은 사용자 환경을 제공하려면 사이트에서 CLS 점수가 0.1 미만이어야 합니다.
CLS 점수 계산법에 대한 내용은 아래 포스팅을 참고 해주세요.

https://nicj.net/cumulative-layout-shift-in-practice/

II. 레이아웃 이동이 발생하는 원인

레이아웃 이동이 발생하는 일반적인 원인은 다음과 같습니다.

  • 치수가 없는 이미지
  • 크기가 없는 광고, 삽입 및 iframe
  • 동적으로 삽입된 콘텐츠
  • FOIT / FOUT을 유발하는 웹 글꼴
  • DOM을 업데이트하기 전에 네트워크 응답을 기다리는 작업

1. 치수가 없는 이미지

이미지 및 비디오 요소에 width 와 height 속성을 항상 포함하거나 또는 CSS를 사용하여 필요한 공간(aspect-ratio-box)을 잡습니다.

이 방법을 사용하면 이미지가 로드되는 동안 브라우저가 문서의 공간을 올바르게 할당할 수 있습니다.

웹 초기(현재도 실무에 많이 사용하지만)에는 브라우저가 이미지를 가져오기 전에 페이지에 충분한 공간이 할당되었는지 확인하기 위해 <img> 태그에 width와 height 속성을 추가했습니다.
이렇게 하면 reflow 과 re-layout 을 최소화할 수 있습니다.

<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

반응형 웹 디자인이 도입되자 개발자들은 widthheight를 생략하고 대신 CSS를 사용하여 이미지 크기를 조정하기 시작했습니다.

img {
  width: 100%; /* or max-width: 100%; */
  height: auto;
}

이 접근 방식의 단점은, 다운로드가 시작되고 브라우저가 크기를 결정할 수 있는 경우에만 이미지를 위한 공간을 할당할 수 있다는 점입니다.
이미지가 로드되면 각 이미지가 화면에 나타나면 reflow 되어 텍스트가 갑자기 화면 아래로 튀어 나가는 것이 일반적이 되었습니다.
이것은 좋지 않은 사용자 경험을 유발합니다.

이것을 방지하기 위해 영역을 미리 확보를 해두게 되는데, 이 때 사용되는 것이 aspect-ratio 입니다.
이미 실무에서 태블릿, 모바일 반응형 대응을 위해 많이 사용되고 있는데요.

치수를 알고 계산을 하는 방식 외에 새로운 방법이 있어 소개드리겠습니다.

1.1 aspect-ratio 속성 사용하기

복잡한 계산없이 간단하게 속성으로 aspect-ratio 를 지정하여 레이아웃 이동을 방지할 수 있습니다.

aspect-ratio 는 CSS4에 새롭게 추가된 속성으로 x/y의 비율을 지정하면 해당 비율로 요소를 나타냅니다.

HTML
<div class="box1">16:9 box</div>
<div class="box2">4:3 box</div>
<div class="box3">1:1 box</div>
CSS
.box1 { aspect-ratio: 16/9; }
.box2 { aspect-ratio: 4/3; }
.box3 { aspect-ratio: 1/1; }

...
[결과]

⚠️ 현재 aspect-ratio 속성은 Firefox 81이상 Chromium 에서 사용할 수 있으며 WebKit(Safari)에도 제공될 예정입니다.
예제 확인 시, 크롬에서는 chrome://flags/ 에서 Experimental Web Platform features 를 Enable 상태로 변경 후 확인해주세요.

● 예제 : test css property : aspect-ratio

컨테이너 안에 이미지가 있는 경우 이미지 크기를 이 컨테이너의 width 값으로 조정할 수 있습니다.

HTML : 이미지가 있는 컨테이너
<div class="box_img">
  <div class="label">▼ 16:9</div>
  <img src="https://img.insight.co.kr/static/2019/03/22/700/za85ysn7ubgzuo3z441x.jpg" width="640" height="390" alt="qkqhro">
</div>

<div class="box2_img">
  <div class="label">▼ 4:3</div>
  <img src="https://img.insight.co.kr/static/2019/03/22/700/za85ysn7ubgzuo3z441x.jpg" width="640" height="390" alt="qkqhro">
</div>

<div class="box3_img">
  <div class="label">▼ 1:1</div>
  <img src="https://img.insight.co.kr/static/2019/03/22/700/za85ysn7ubgzuo3z441x.jpg" width="640" height="390" alt="qkqhro">
</div>
css : 이미지가 있는 컨테이너
div[class^="box"] {
  display: inline-block;
  width: 300px;
  margin-bottom: 40px;
  padding: 10px 20px;
  vertical-align: top;
}

/* 이미지 속성에 height: auto; 를 설정하여
이미지 높이가 고정된 값(inline으로 선언한 height값)이 되지 않도록 합니다. */
img {
  width: 100%;
  height: auto;
}

/* 컨테이너에 원하는 aspect-ratio 선언 */
.box1_img img { aspect-ratio: 16/9; }
.box2_img img { aspect-ratio: 4/3; }
.box3_img img { aspect-ratio: 1/1; }
...
[결과]

● 예제 : test css property : aspect-ratio with img

모든 브라우저의 User Agent Stylesheet 는 요소의 기존 width 및 height 속성에 따라 기본 aspect-ratio 를 추가할 수도 있습니다.

img {
	aspect-ratio: attr(width) / attr(height);
}

이미지가 로드되기 전에 width 및 height 속성을 기준으로 aspect-ratio 를 계산합니다. 이 정보는 레이아웃 계산을 시작할 때 제공됩니다.
이미지가 특정 width(예: width: 100%)로 표시되는 즉시 aspect-ratio를 사용하여 높이를 계산합니다.

1.2 반응형 이미지 처리

img 태그의 width 및 height 속성을 설정할 수 있도록 각 이미지는 같은 aspect-ratio 를 사용해야 합니다.

또 기존의 img 태그는 오직 하나의 소스 파일만 제시하도록 되어 있는데, srcset과 sizes라는 두 가지 새로운 속성을 사용해 브라우저가 이미지를 나타내는데에 도움이 되는 몇 가지 추가 소스 이미지와 힌트를 제공 할 수 있습니다.

<img width="1000" height="1000"
src="puppy-1000.jpg"
srcset="puppy-1000.jpg 1000w,
puppy-2000.jpg 2000w,
puppy-3000.jpg 3000w"
alt="Puppy with balloons"/>

그리고 하나의 이미지가 좁은 뷰포트에 잘려진 일부 이미지로 보여지게도 할 수 있습니다.

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg">
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg">
  <img src="puppy-800w.jpg" alt="Puppy with balloons">
</picture>

이것은 Art direction problem 에 따른 이미지 처리 방법입니다. 자세한 내용은 아래 링크 참고 부탁드립니다.

https://developer.mozilla.org/ko/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction

2. 크기가 정의되지 광고, Embed 및 iframe

2.1 광고

광고는 웹에서 레이아웃 이동에 가장 큰 영향을 주는 요소 중 하나입니다.

광고 크기는 높은 클릭율과 성능,수익에 관련이 있기 때문에 광고 게시자는 종종 동적인 광고 크기를 지원합니다.
이러한 동적인 광고 크기는 현재 보고 있는 콘텐츠를 페이지 아래로 밀어버릴 수 있어 사용자 환경이 최적화되지 않을 수 있습니다.

광고의 life-cycle 동안 많은 지점에서 레이아웃 이동이 발생할 수 있습니다.

  • 사이트가 DOM에 광고 컨테이너를 삽입할 때
  • 사이트에서 자사 코드를 사용하여 광고 컨테이너의 크기를 조정할 때
  • 광고 태그 라이브러리가 로드될 때(광고 컨테이너의 크기가 조정될 때)
  • 광고가 컨테이너를 채울 때(최종 광고의 크기가 다른 경우 크기가 조정됨)

좋은 소식은 사이트가 모범 사례를 따라 광고 이동을 줄일 수 있다는 것입니다. 다음과 같은 방법으로 레이아웃 변화를 완화할 수 있습니다.

  • 광고 슬롯을 위한 공간을 정적으로 확보합니다.
    즉, 광고 태그 라이브러리가 로드되기 전에 요소의 스타일을 지정합니다.
    콘텐츠 흐름에 광고를 배치하는 경우 슬롯 크기를 확보하여 레이아웃 이동을 방지 하십시오.
  • 뷰포트 상단 근처에 고정되지 않은 광고를 배치할 때 주의하세요.
    아래 예시처럼 광고를 월드 비전 로고 아래로 이동하고 슬롯을 위한 충분한 공간을 확보하는 것이 좋습니다.
  • placeholder 를 표시하여 광고 슬롯이 표시될 때, 반환된 광고가 없는 경우 확보된 공간을 축소하지 마십시오.
  • 광고 슬롯에 가능한 가장 큰 크기를 확보하여 레이아웃 이동을 방지하십시오.
    더 작은 광고 소재가 슬롯을 채우면 빈 공간이 생길 위험이 있습니다.
  • 이전 데이터를 기반으로 광고 슬롯에 가장 적합한 크기를 선택합니다.

일부 사이트는 처음에 슬롯을 접으면 광고 슬롯이 채워지지 않을 때 레이아웃 이동을 줄일 수 있습니다.
광고를 직접 제어하지 않는 한, 매번 정확한 크기를 선택하는 쉬운 방법은 없습니다.

2.1.1 광고 슬롯을 위한 정적 공간 확보

태그 라이브러리에 전달되는 같은 크기의 슬롯 DOM 요소를 정적으로 스타일링합니다.
이렇게 하면 라이브러리가 로드될 때 레이아웃 이동을 야기하지 않을 수 있습니다.
이렇게 하지 않으면 페이지 레이아웃 로드 후에 라이브러리가 슬롯 요소의 크기를 변경할 수 있습니다.

또한, 더 작은 광고 서비스 사이즈를 고려하세요.
더 작은 광고가 제공되는 경우 게시자는 레이아웃 이동을 방지하기 위해 더 큰 컨테이너의 스타일을 지정할 수 있습니다.
이 접근 방식의 단점은 빈 공간의 양이 증가한다는 것이므로 여기서 절충안을 염두에 두십시오.

2.1.2 뷰포트 상단 근처에 광고 배치하지 않기

뷰포트 상단 근처에 있는 광고는 가운데에 있는 광고보다 더 큰 레이아웃 이동을 유발할 수 있습니다.
왜냐하면, 상단의 광고는 일반적으로 더 많은 콘텐츠를 아래로 가지고 있기 때문에 광고가 변화를 일으킬 때 더 많은 요소가 움직인다는 것을 의미합니다.
반대로 뷰포트의 중간 근처에 있는 광고는 위의 콘텐츠가 이동할 가능성이 적은 만큼 많은 요소를 이동하지 않을 수 있습니다.

2.2 Embeds and iframes

Embed 가능한 위젯을 사용하면 페이지에 삽입 가능한 웹 콘텐츠(예 : YouTube의 비디오, Google Maps의 지도, 소셜 미디어 게시물 등)를 포함할 수 있습니다.
이러한 Embeds는 다음과 같은 여러 형식을 취할 수 있습니다.

  • HTML fallback 및 fallback을 embed로 변환하는 JavaScript 태그
  • inline HTML snippet
  • iframe 삽입

이러한 Embeds는 Embed 규모가 얼마나 될 것인지 미리 알지 못하는 경우가 많습니다.

예: 소셜 미디어 포스트의 경우 embed 이미지, 비디오, 여러 줄의 텍스트 등이 있는지)

따라서 Embeds를 제공하는 플랫폼은 Embeds를 위한 항상 충분한 공간을 확보하지는 않으며, 최종적으로 로드할 때 레이아웃 이동이 발생할 수 있습니다.

이 문제를 해결하려면 placeholder 또는 fallback을 삽입할 충분한 공간을 미리 계산하여 CLS를 최소화할 수 있습니다.
삽입에 사용할 수 있는 워크플로우는 다음과 같습니다.

  • 브라우저 개발자 도구로 검사하여 최종 Embed 높이 값 확보
  • Embed 가 로드되면 포함된 iframe 의 크기를 콘텐츠에 맞게 조정

치수를 기록하고 그에 따라 Embeds 할 자리의 placeholder 의 스타일을 지정합니다.
다만 미디어쿼리를 사용하여 서로 다른 구성요소 간의 ad / placeholder 크기의 미묘한 차이를 고려해야 할 수 있습니다.

3. 동적 콘텐츠

사용자 상호 작용에 대한 응답이 없는 경우 기존 콘텐츠 위에 새 콘텐츠를 삽입하지 않도록 합니다.

사이트를 로드하려고 할 때 뷰포트의 맨 위 / 아래에 나타나는 UI로 인해 레이아웃 이동을 경험했을 것입니다.
이는 광고와 마찬가지로 페이지의 나머지 콘텐츠를 이동시키는 배너 및 폼에서 종종 발생합니다.

이러한 유형의 UI 제공 프로그램을 표시해야 하는 경우 미리 뷰포트에 충분한 공간을 확보하여(예: placeholder 또는 skeleton UI 사용) 로드할 때 페이지의 콘텐츠가 이동하지 않도록 합니다.

4. FOUT / FOIT를 유발하는 웹 글꼴

웹 글꼴을 다운로드하고 렌더링하면 다음 두 가지 방식으로 레이아웃이 변경될 수 있습니다.

  • FOUT : 웹 폰트가 적용되지 않은 Fallback 폰트 상태(unstyled)에서 폰트가 바뀌면서 텍스트 번쩍임이 일어남
  • FOIT : 웹 폰트가 적용되지 않은 텍스트가 보이지 않는 상태(invisible)에서 폰트가 바뀌면서 텍스트 번쩍임이 일어남

다음 도구를 사용하면 이를 최소화할 수 있습니다.

font-display를 사용하면 autoswapblockfallbackoptional 등의 값을 사용하여 사용자 정의 폰트의 렌더링 동작을 수정할 수 있습니다.
그러나 위의 방법의 하나로 이러한 값( optional 제외)이 모두 re-layout 될 수 있습니다.
Font Loading API 를 사용하면 필요한 폰트를 가져오는 데 걸리는 시간을 줄일 수 있습니다.

Chrome 83부터 다음도 권장할 수 있습니다.

  • 주요 웹 글꼴에 <link el=preload>를 사용하면 사전 로드된 폰트가 첫 번째 paint와 일치할 가능성이 커지며, 이 경우 레이아웃 이동이 없습니다.
  • <link el=preload>와 font-display: optional을 조합하여 사용할 수 있습니다.

자세한 내용은 Prevent layout shifting and flashes of invisible text (FOIT) by preloading optional fonts 를 참고해주세요.

5. 애니메이션

layout shift 를 트리거하는 속성 애니메이션보다 transform 애니메이션을 변환하는 것이 좋습니다

CSS 속성값을 변경하면 브라우저가 이러한 변경에 반응해야 할 수 있습니다.
box-shadow와 box-sizing같은 여러 속성이 re-layout 과 paint 를 트리거합니다.
많은 CSS 속성은 비용이 적게 드는 방식으로 변경될 수 있습니다.

layout shift 를 트리거하는 CSS 속성에 대한 자세한 내용은 CSS Triggers 및 high-performance-animations 을 참조하십시오.

III. 개발자 도구를 이용한 CLS 측정

Lighthouse 6.0 이상에는 lab setting에서 CLS 측정에 대한 지원이 포함됩니다.
이 릴리스는 또한 가장 많은 layout shift 를 유발하는 노드를 강조합니다.

개발자 도구의 Performance 패널은 Chrome 84를 기준으로 Experience Section의 layout shift 를 강조 표시합니다.
Layout Shift 기록의 Summary 뷰에는 영향을 받는 영역을 보여주는 오버레이 영역과 CLS 점수가 포함됩니다.

Performance 패널에서 새 추적을 기록하면 결과의 Experience 섹션에 layout shift 를 기록을 표시하는 빨간색 색상이 지정된 막대가 채워집니다. 기록(빨간 막대)을 클릭하면 영향을 받는 요소를 확인할 수 있습니다.

Chrome 사용자 환경 보고서를 사용하여 원본 수준에서 집계된 실제 CLS를 측정할 수도 있습니다.


글을 마치며 ✍️

그동안 대부분 속도나 데이터크기 등 정량화된 수치를 이용하여 성능을 측정했는데,
이렇게 사용자 경험적인 측면으로 시각적 안정화를 고민하고 레이아웃 변경 빈도를 수치화 한다는 것이 흥미로웠습니다.
내용의 대부분이 우리가 많이 알고 있는 웹 성능 최적화 내용과 상당부분 동일한 것을 볼 수 있었습니다.

크롬 개발자도구를 이용해 간단한 측정으로 성능을 확인하여
마크업에서의 수정으로도 해결할 수 있는 부분이 많기 때문에 서비스 개선에 참고하시면 좋을 것 같습니다.


출처 및 참고자료


0개의 댓글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다