Cumulative Layout Shift (CLS)

브라우저 지원

  • 77
  • 79
  • x
  • x

소스

예기치 않은 레이아웃 변경은 텍스트를 읽는 동안 원래 자리를 잃거나 텍스트가 갑작스럽게 이동하면 잘못된 링크나 버튼을 클릭하는 등 다양한 방식으로 사용자 환경을 방해할 수 있습니다. 경우에 따라 심각한 손상이 발생할 수 있습니다.

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

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

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

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

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

CLS란 무엇인가요?

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

레이아웃 변경은 표시되는 요소가 하나의 렌더링된 프레임에서 다음 프레임으로 위치를 변경할 때마다 발생합니다. 개별 레이아웃 변경 점수 계산 방법에 관한 자세한 내용은 이 가이드의 뒷부분에서 다룹니다.

세션 창이라고 하는 레이아웃 변경 버스트는 하나 이상의 개별 레이아웃 변경이 각 전환 사이의 간격이 1초 미만이고 총 창 지속 시간이 최대 5초인 경우 빠르게 연달아 발생하는 경우를 말합니다.

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

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

좋은 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입니다.

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

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

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

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

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

거리 비율은 보라색 화살표로 표시되어 있습니다. 녹색 상자가 표시 영역의 약 14% 아래로 이동했으므로 거리 비율0.14입니다.

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

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

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

위 이미지의 첫 번째 프레임에는 알파벳순으로 정렬된 동물에 대한 API 요청 결과가 4개 있습니다. 두 번째 프레임에서는 더 많은 결과가 정렬된 목록에 추가됩니다.

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

다시 점선으로 된 빨간색 직사각형은 영역 앞뒤에 있는 세 가지 불안정한 요소의 합집합을 나타냅니다. 여기서는 표시 영역 영역의 약 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 측정

JavaScript에서 CLS를 측정하려면 예상치 못한 layout-shift 항목을 세션으로 그룹화하고 최대 세션 값을 계산해야 합니다. CLS 계산 방법에 관한 참조 구현이 포함된 web vitals JavaScript 라이브러리 소스 코드를 참고하세요.

대부분의 경우 페이지 로드 취소 시의 현재 CLS 값이 해당 페이지의 최종 CLS 값이지만, 다음 섹션에서 설명하는 몇 가지 중요한 예외도 있습니다. web vitals JavaScript 라이브러리는 웹 API의 제한사항 내에서 이러한 요소를 최대한 많이 고려합니다.

측정항목과 API의 차이점

  • 페이지가 백그라운드에서 로드되거나 브라우저가 콘텐츠를 그리기 전에 백그라운드에 있다면 CLS 값을 보고해서는 안 됩니다.
  • 페이지가 뒤로-앞으로 캐시에서 복원되면 사용자가 이를 별개의 페이지 방문으로 경험하므로 CLS 값을 0으로 재설정해야 합니다.
  • API는 iframe 내에서 발생하는 이동에 대해 layout-shift 항목을 보고하지 않지만 측정항목은 페이지 사용자 환경의 일부이므로 보고하지 않습니다. 이는 CrUX와 RUM의 차이점으로 나타날 수 있습니다. CLS를 올바르게 측정하려면 CLS를 고려해야 합니다. 하위 프레임은 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 그룹에 작성해 주세요.