Cumulative Layout Shift (CLS)

브라우저 지원

  • 77
  • 79
  • x
  • x

소스

레이아웃 변경 횟수 (CLS)는 안정적인 Core Web Vitals 측정항목입니다. 이는 시각적 안정성을 측정하는 중요한 사용자 중심 측정항목입니다. 사용자가 예기치 않은 레이아웃 변경을 경험하는 빈도를 수치화하는 데 도움이 되기 때문입니다. CLS가 낮으면 페이지가 유쾌해집니다.

예기치 않은 레이아웃 변경으로 인해 텍스트가 갑자기 움직이면 읽는 동안 원래 있던 위치를 잃거나 잘못된 링크나 버튼을 클릭하게 되는 등 여러 가지 방식으로 사용자 환경에 지장을 줄 수 있습니다. 경우에 따라, 이렇게 하면 심각한 손상이 발생할 수 있습니다.

레이아웃이 갑자기 변경되면 사용자가 취소하려고 했던 대량 주문을 확인할 수 있습니다.

일반적으로 리소스가 비동기식으로 로드되거나 기존 콘텐츠보다 먼저 페이지에 DOM 요소가 동적으로 추가될 때 페이지 콘텐츠의 예기치 않은 이동이 발생합니다. 크기가 알 수 없는 이미지 또는 동영상, 대체 글꼴보다 크거나 작게 렌더링되는 글꼴, 동적으로 크기가 조절되는 서드 파티 광고 또는 위젯이 이동의 원인일 수 있습니다.

개발 단계에서 사이트가 작동하는 방식과 사용자가 사이트를 이용하는 방식의 차이로 인해 문제가 악화됩니다. 예를 들면 다음과 같습니다.

  • 개인 맞춤 콘텐츠 또는 서드 파티 콘텐츠는 개발과 프로덕션에서 서로 다르게 동작하는 경우가 많습니다.
  • 테스트 이미지는 이미 개발자의 브라우저 캐시에 있는 경우가 많지만 최종 사용자가 로드하는 데 시간이 더 오래 걸립니다.
  • 로컬에서 실행되는 API 호출은 매우 빠르게 실행되어 개발 시 눈에 띄지 않는 지연이 프로덕션 단계에서 크게 발생할 수 있습니다.

레이아웃 변경 횟수 (CLS) 측정항목은 실제 사용자에게 발생하는 빈도를 측정하여 이 문제를 해결하는 데 도움이 됩니다.

CLS란 무엇인가요?

CLS는 페이지 수명 동안 발생하는 모든 예상치 못한 레이아웃 변경에 관한 레이아웃 변경 점수의 가장 큰 버스트를 측정한 것입니다.

레이아웃 변경은 표시되는 요소가 하나의 렌더링된 프레임에서 다음 프레임으로 위치를 변경할 때마다 발생합니다. 이러한 이동이 측정되는 방법에 관한 자세한 내용은 레이아웃 변경 점수를 참고하세요.

세션 윈도우라고 하는 레이아웃 변경 버스트는 하나 이상의 개별 레이아웃 변경이 최대 5초 간 각 이동 사이에 1초 미만의 빠른 연속으로 발생하는 경우를 말합니다.

가장 큰 버스트는 해당 기간 내의 모든 레이아웃 변경의 최대 누적 점수가 있는 세션 기간입니다.

세션 기간의 예 파란색 막대는 각 개별 레이아웃 변경의 점수를 나타냅니다.

좋은 CLS 점수란 무엇인가요?

우수한 사용자 환경을 제공하려면 사이트의 CLS 점수가 0.1 이하여야 합니다. 대부분의 사용자에게 이 목표에 도달할 수 있도록 페이지 로드의 75번째 백분위수를 휴대기기와 데스크톱 기기별로 분류하여 측정하는 것이 좋습니다.

올바른 CLS 값은 0.1 이하이고, 좋지 않은 값은 0.25보다 크며, 그 사이의 값은 개선이 필요합니다.
올바른 CLS 값은 0.1 이하입니다. 잘못된 값은 0.25보다 큽니다.

이 추천의 배경이 되는 연구 및 방법론에 관한 자세한 내용은 코어 웹 바이탈 측정항목 기준점 정의를 참고하세요.

레이아웃 변경 세부정보

레이아웃 이동은 Layout Instability API에 의해 정의됩니다. 이 API는 표시 영역 내에 표시되는 요소가 시작 위치 (예: 기본 쓰기 모드의 상단 및 왼쪽 위치)를 두 프레임 사이에서 변경할 때마다 layout-shift 항목을 보고합니다. 시작 위치가 변경되는 요소는 불안정한 요소로 간주됩니다.

레이아웃 변경은 기존 요소가 시작 위치를 변경할 때만 발생합니다. DOM에 새 요소가 추가되거나 기존 요소의 크기가 변경되면, 변경으로 인해 보이는 다른 요소가 시작 위치를 변경하는 경우에만 레이아웃 변경으로 집계됩니다.

레이아웃 변경 점수

레이아웃 변경 점수를 계산하기 위해 브라우저는 표시 영역 크기 및 렌더링된 두 프레임 간 표시 영역 내 불안정한 요소의 이동을 고려합니다. 레이아웃 변경 점수는 이동의 두 가지 측정값, 즉 영향 비율거리 비율의 곱입니다.

layout shift score = impact fraction * distance fraction

영향 비율

영향 비율은 불안정한 요소가 두 프레임 간의 표시 영역 영역에 미치는 영향을 측정합니다.

특정 프레임의 영향 비율은 해당 프레임과 이전 프레임에서 불안정한 모든 요소의 표시 가능 영역을 표시 영역의 총 면적에 대한 비율로 조합한 것입니다.

불안정한 요소 하나가 있는 영향 비율 예
요소의 위치가 변경되면 이전 위치와 현재 위치가 모두 영향 비율에 반영됩니다.

이 이미지는 한 프레임에서 표시 영역의 절반을 차지하는 요소를 보여줍니다. 다음 프레임에서는 요소가 표시 영역 높이의 25% 씩 아래로 이동합니다. 빨간색 파선 직사각형은 두 프레임에서 요소가 표시되는 영역을 나타냅니다. 이 경우 총 표시 영역의 75% 에 해당하므로 영향 비율은 0.75입니다.

이동 거리 비율

레이아웃 변경 점수 방정식의 다른 부분은 표시 영역을 기준으로 불안정한 요소가 이동한 거리를 측정합니다. 거리 비율은 프레임에서 불안정한 요소가 이동한 가장 큰 가로 또는 세로 거리를 표시 영역의 최대 크기 (너비 또는 높이 중 더 큰 값)로 나눈 값입니다.

불안정한 요소가 하나 있는 거리 비율의 예
거리 비율은 표시 영역을 가로질러 요소가 이동한 거리를 측정합니다.

이 예에서 가장 큰 표시 영역 크기는 높이이고 불안정한 요소가 표시 영역 높이의 25% 만큼 이동했으므로 거리 비율이 0.25가 됩니다.

0.75의 영향 비율과 0.25의 거리 비율은 0.75 * 0.25 = 0.1875의 레이아웃 변경 점수를 생성합니다.

다음 예는 기존 요소에 콘텐츠를 추가할 때 레이아웃 변경 점수에 어떤 영향을 미치는지 보여줍니다.

안정적이고 _불안정한 요소_가 여러 개 있는 레이아웃 변경 예
회색 상자의 하단에 버튼을 추가하면 녹색 상자가 아래로 밀려 일부가 표시 영역을 벗어납니다.

이 예에서 회색 상자는 크기가 변경되지만 시작 위치는 변경되지 않으므로 불안정한 요소가 아닙니다.

이전에는 'Click Me!' 버튼이 DOM에 없었으므로 시작 위치도 변경되지 않습니다.

녹색 상자의 시작 위치는 바뀌지만, 표시 영역의 일부가 표시 영역 밖으로 이동되고 보이지 않는 영역은 영향 비율을 계산할 때 고려되지 않습니다. 두 프레임에서 녹색 상자에 표시되는 영역의 합집합(빨간색 파선 직사각형)은 첫 번째 프레임의 녹색 상자의 합집합(표시 영역의 50%)과 동일합니다. 영향 비율은 0.5입니다.

거리 비율은 파란색 화살표로 표시됩니다. 녹색 상자가 표시 영역의 약 14% 아래로 이동했으므로 거리 비율은 0.14입니다.

레이아웃 변경 점수는 0.5 x 0.14 = 0.07입니다.

다음 예에서는 불안정한 요소 여러 개가 페이지의 레이아웃 변경 점수에 미치는 영향을 보여줍니다.

안정적인 요소와 _불안정한 요소_ 및 표시 영역 클리핑을 사용한 레이아웃 변경의 예
정렬된 목록에 더 많은 이름이 표시되면 알파벳순으로 기존 이름이 이동합니다.

목록의 첫 번째 항목 ('Cat')은 프레임 간에 시작 위치를 변경하지 않으므로 안정적입니다. 목록에 추가된 새 항목은 이전에 DOM에 없었으므로 시작 위치도 변경되지 않습니다. 하지만 'Dog', 'Horse', 'Zebra'라는 라벨이 지정된 항목은 모두 시작 위치를 변경하여 요소가 불안정해집니다.

다시 빨간색 파선 직사각형은 전환 전후에 불안정한 세 요소의 영역을 나타냅니다. 여기서는 표시 영역 영역의 약 60% 에 해당합니다 (0.60의 영향 비율).

화살표는 불안정한 요소가 시작 위치에서 이동한 거리를 나타냅니다. 파란색 화살표로 표시되는 'Zebra' 요소는 표시 영역 높이의 약 30% 만큼 가장 많이 이동했습니다. 그러면 이 예시에서 거리 비율이 0.3가 됩니다.

레이아웃 변경 점수는 0.60 x 0.3 = 0.18입니다.

예상된 레이아웃 변경과 예기치 않은 레이아웃 변경 비교

모든 레이아웃 변경이 나쁜 것은 아닙니다. 실제로 많은 동적 웹 애플리케이션에서 페이지 내 요소의 시작 위치가 변경됩니다. 레이아웃 변경은 사용자가 예상하지 않는 경우에만 좋지 않습니다.

사용자가 시작한 레이아웃 변경

사용자 상호작용 (예: 링크 클릭 또는 탭, 버튼 누르기, 검색창 입력)에 반응하여 발생하는 레이아웃 변경은 일반적으로 문제가 되지 않습니다. 단, 상호작용과 가까운 위치에서 전환이 사용자에게 명확해 질 수 있는 경우에 한합니다.

예를 들어 사용자 상호작용으로 인해 완료되는 데 시간이 걸릴 수 있는 네트워크 요청이 트리거되는 경우 요청 완료 시 레이아웃 변경을 피할 수 있도록 즉시 공간을 만들고 로드 표시기를 표시하는 것이 가장 좋습니다. 사용자가 무언가가 로드되고 있음을 깨닫지 못하거나 리소스가 준비될 시점을 알지 못하는 경우 기다리는 동안 다른 항목을 클릭하려고 할 수 있고 첫 번째 요소가 로드되면 다른 요소가 하위 요소에서 벗어날 수 있습니다.

사용자 입력의 500밀리초 이내에 발생하는 레이아웃 변경은 hadRecentInput 플래그가 설정되므로 계산에서 제외할 수 있습니다.

애니메이션 및 전환

애니메이션과 전환은 잘 실행되면 사용자를 놀라게 하지 않고 페이지의 콘텐츠를 업데이트할 수 있는 좋은 방법입니다. 페이지에서 갑자기 예기치 않게 이동하는 콘텐츠는 거의 항상 사용자 경험을 저하시킵니다. 그러나 한 위치에서 다른 위치로 점진적이고 자연스럽게 이동하는 콘텐츠는 사용자가 현재 상황을 더 잘 이해하고 상태 변경 간에 전환하는 데 도움이 될 수 있습니다.

애니메이션은 일부 사이트 방문자에게 상태 또는 주의 문제를 일으킬 수 있으므로 prefers-reduced-motion 브라우저 설정을 준수해야 합니다.

CSS transform 속성을 사용하면 레이아웃 변경을 트리거하지 않고도 요소에 애니메이션을 적용할 수 있습니다.

  • heightwidth 속성을 변경하는 대신 transform: scale()를 사용합니다.
  • 요소를 이동하려면 top, right, bottom 또는 left 속성을 변경하는 대신 transform: translate()를 사용합니다.

CLS 측정 방법

CLS는 실험실 또는 현장에서 측정할 수 있으며 다음 도구에서 사용할 수 있습니다.

현장 도구

실습 도구

JavaScript에서 레이아웃 변경 측정

JavaScript에서 레이아웃 변경을 측정하려면 Layout Instability API를 사용하세요.

다음 예에서는 PerformanceObserver를 만들어 layout-shift 항목을 콘솔에 로깅하는 방법을 보여줍니다.

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('Layout shift:', entry);
  }
}).observe({type: 'layout-shift', buffered: true});

자바스크립트에서 CLS 측정

자바스크립트에서 CLS를 측정하려면 세션에 로그인한 예상치 못한 layout-shift 항목을 그룹화하고 최대 세션 값을 계산합니다. 참조 구현은 web vitals JavaScript 라이브러리 소스 코드를 참고하세요.

대부분의 경우 페이지가 언로드되는 시점의 CLS 값이 해당 페이지의 최종 CLS 값이지만, 다음 섹션에는 몇 가지 중요한 예외가 나열되어 있습니다. web vitals JavaScript 라이브러리는 웹 API의 제한사항 내에서 이러한 기능을 최대한 고려합니다.

측정항목과 API의 차이점

  • 페이지가 백그라운드에서 로드되거나 브라우저가 콘텐츠를 그리기 전에 백그라운드에 있다면 CLS 값을 보고하면 안 됩니다.
  • 페이지가 뒤로 또는 앞으로 캐시에서 복원되면 CLS 값은 0으로 재설정되어야 합니다. 사용자가 이를 고유한 페이지 방문으로 경험하기 때문입니다.
  • API는 iframe 내에서 발생하는 이동과 관련해 layout-shift 항목을 보고하지 않지만, 측정항목은 페이지의 사용자 환경의 일부이므로 보고하지 않습니다. 이는 CrUX와 RUM의 차이점으로 나타날 수 있습니다. CLS를 제대로 측정하려면 iframe을 포함해야 합니다. 하위 프레임은 API를 사용하여 집계를 위해 layout-shift 항목을 상위 프레임에 보고할 수 있습니다.

이러한 예외 외에도 CLS는 페이지의 전체 수명을 측정하므로 훨씬 더 복잡합니다.

  • 사용자는 며칠, 몇 주 또는 몇 개월 등 매우 오랫동안 탭을 열어 둘 수 있습니다. 실제로 사용자가 탭을 닫지 않을 수도 있습니다.
  • 모바일 운영체제에서 브라우저는 일반적으로 백그라운드 탭의 페이지 로드 취소 콜백을 실행하지 않으므로 '최종' 값을 보고하기가 어렵습니다.

이러한 사례를 처리하려면 로드 취소 시뿐만 아니라 페이지가 백그라운드에 있을 때마다 시스템에서 CLS를 보고하는 것이 좋습니다. visibilitychange 이벤트는 이 두 시나리오를 모두 처리합니다. 그러면 이 데이터를 수신하는 애널리틱스 시스템이 백엔드에서 최종 CLS 값을 계산해야 합니다.

개발자는 이러한 모든 사례를 직접 암기하고 씨름하는 대신 web-vitals JavaScript 라이브러리를 사용하여 CLS를 측정할 수 있습니다. CLS는 iframe 사례를 제외하고 여기에서 언급된 모든 내용을 고려합니다.

import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);

CLS를 개선하는 방법

필드에서 레이아웃 변경을 식별하고 실험실 데이터를 사용하여 최적화하는 방법에 관한 자세한 내용은 CLS 최적화 가이드를 참고하세요.

추가 리소스

변경 로그

측정항목을 측정하는 데 사용되는 API에서 버그가 발견되기도 하고 측정항목 자체의 정의에서도 버그가 발견되기도 합니다. 그 결과, 때때로 변경이 필요하며, 이러한 변경은 내부 보고서와 대시보드에서 개선 또는 회귀로 표시될 수 있습니다.

이를 관리하는 데 도움이 되도록 이러한 측정항목의 구현 또는 정의에 대한 모든 변경사항이 이 변경 로그에 표시됩니다.

이러한 측정항목에 관한 의견이 있으면 web-vitals-feedback Google 그룹에 제공해 주세요.