지금까지 웹 개발자는 웹페이지의 주요 콘텐츠가 얼마나 빨리 로드되고 사용자에게 표시되는지 측정하는 데 어려움을 겪었습니다. 로드 또는 DOMContentLoaded와 같은 이전 측정항목은 사용자가 화면에 보는 내용과 반드시 일치하지 않으므로 제대로 작동하지 않습니다. 또한 최초 콘텐츠 페인트(FCP)와 같은 최신 사용자 중심 성능 측정항목은 로드 환경의 시작 부분만 포착합니다. 페이지에 스플래시 화면이 표시되거나 로드 표시기가 표시되는 순간은 사용자와 관련성이 높지 않습니다.
이전에는 초기 페인트 후 로드 환경을 더 많이 포착하는 데 도움이 되는 첫 번째 의미 있는 페인트(FMP) 및 속도 색인(SI)(둘 다 Lighthouse에서 사용 가능)과 같은 성능 측정항목을 권장했습니다. 하지만 이러한 측정항목은 복잡하고 설명하기 어렵고 종종 잘못된 결과를 제공합니다. 즉, 페이지의 기본 콘텐츠가 로드된 시점을 여전히 식별하지 못합니다.
W3C Web Performance Working Group(W3C 웹 성능 실무 그룹)의 토론 및 Google에서 진행한 연구에 따르면 페이지의 주요 콘텐츠가 로드되는 시점을 측정하는 더 정확한 방법은 가장 큰 요소가 렌더링되는 시점을 확인하는 것입니다.
LCP란 무엇인가요?
LCP는 사용자가 페이지로 처음 이동한 시점을 기준으로 표시 영역에 표시되는 가장 큰 이미지, 텍스트 블록 또는 동영상의 렌더링 시간을 보고합니다.
좋은 LCP 점수란 무엇인가요?
만족스러운 사용자 환경을 제공하기 위해 사이트의 최대 콘텐츠 페인트가 2.5초 이하가 되도록 노력해야 합니다. 대부분의 사용자가 이 목표를 달성하도록 하려면 모바일 및 데스크톱 기기별로 분류된 페이지 로드의 75번째 백분위수를 측정하는 것이 좋습니다.
어떤 요소가 고려되나요?
현재 최대 콘텐츠 렌더링 시간 API에 지정된 대로 최대 콘텐츠 렌더링 시간에 고려되는 요소 유형은 다음과 같습니다.
<img>
요소(첫 번째 프레임 표시 시간은 GIF 또는 애니메이션 PNG와 같은 애니메이션 콘텐츠에 사용됨)<svg>
요소 내의<image>
요소<video>
요소(포스터 이미지 로드 시간 또는 동영상의 첫 번째 프레임 표시 시간 중 더 빠른 값 사용)- CSS 그라데이션이 아닌
url()
함수를 사용하여 로드된 배경 이미지가 있는 요소 - 텍스트 노드 또는 기타 인라인 수준 텍스트 요소 하위 요소가 포함된 블록 수준 요소
처음에는 간단하게 시작할 수 있도록 요소를 이 제한된 세트로 제한한 것은 의도적인 조치였습니다. 향후 더 많은 연구가 진행됨에 따라 추가 요소 (예: 전체 <svg>
지원)가 추가될 수 있습니다.
LCP 측정에서는 일부 요소만 고려할 뿐만 아니라 휴리스틱을 사용하여 사용자에게 '콘텐츠가 없는' 것으로 보일 수 있는 특정 요소를 제외합니다. Chromium 기반 브라우저의 경우 다음이 포함됩니다.
- 사용자에게 표시되지 않는 불투명도가 0인 요소
- 전체 표시 영역을 차지하며 콘텐츠가 아닌 배경으로 간주될 수 있는 요소
- 페이지의 실제 콘텐츠를 반영하지 않을 가능성이 높은 엔트로피가 낮은 자리표시자 이미지 또는 기타 이미지
브라우저는 가장 큰 콘텐츠가 포함된 요소가 무엇인지에 대한 사용자의 기대에 부응하기 위해 이러한 휴리스틱을 계속해서 개선해 나갈 것입니다.
이러한 '콘텐츠가 포함된' 휴리스틱은 콘텐츠가 포함된 첫 페인트(FCP)에서 사용하는 휴리스틱과 다를 수 있습니다. FCP에서는 자리표시자 이미지나 전체 표시 영역 이미지와 같이 LCP 후보가 될 수 없는 요소도 고려할 수 있습니다. 두 측정항목 모두 이름에 'contentful'을 사용하지만 목적은 다릅니다. FCP는 모든 콘텐츠가 화면에 페인트되는 시점을 측정하고 LCP는 기본 콘텐츠가 페인트되는 시점을 측정하므로 LCP는 더 선택적입니다.
요소의 크기는 어떻게 결정되나요?
LCP에 대해 보고되는 요소의 크기는 일반적으로 표시 영역 내에서 사용자에게 표시되는 크기입니다. 요소가 표시 영역 밖으로 확장되거나 요소가 잘리거나 표시되지 않는 오버플로가 있는 경우 이러한 부분은 요소의 크기에 포함되지 않습니다.
내장 크기에서 크기가 조정된 이미지 요소의 경우 보고되는 크기는 표시되는 크기 또는 기본 크기 중 더 작은 크기입니다.
텍스트 요소의 경우 LCP는 모든 텍스트 노드를 포함할 수 있는 가장 작은 직사각형만 고려합니다.
모든 요소에서 LCP는 CSS를 사용하여 적용된 여백, 패딩, 테두리를 고려하지 않습니다.
LCP는 언제 보고되나요?
웹페이지는 단계적으로 로드되는 경우가 많으므로 페이지에서 가장 큰 요소가 변경될 수도 있습니다.
이러한 변경 가능성을 처리하기 위해 브라우저는 첫 번째 프레임을 페인팅하자마자 가장 큰 콘텐츠가 포함된 요소를 식별하는 largest-contentful-paint
유형의 PerformanceEntry
를 전달합니다. 그러나 후속 프레임을 렌더링한 후에는 가장 큰 콘텐츠 요소가 변경될 때마다 다른 PerformanceEntry
를 전달합니다.
예를 들어 텍스트와 히어로 이미지가 있는 페이지에서 브라우저는 처음에 텍스트를 렌더링할 수도 있습니다. 이때 브라우저는 element
속성이 <p>
또는 <h1>
를 참조할 가능성이 높은 largest-contentful-paint
항목을 전달합니다. 나중에 히어로 이미지 로드가 완료되면 두 번째 largest-contentful-paint
항목이 전달되고 element
속성이 <img>
를 참조합니다.
요소는 렌더링되고 사용자에게 표시된 후에만 가장 큰 콘텐츠 요소로 간주될 수 있습니다. 아직 로드되지 않은 이미지는 '렌더링된' 것으로 간주되지 않습니다. 글꼴 차단 기간 동안 웹 글꼴을 사용하는 텍스트 노드도 마찬가지입니다. 이 경우 더 작은 요소가 콘텐츠가 포함된 최대 요소로 보고될 수 있지만 더 큰 요소의 렌더링이 완료되는 즉시 다른 PerformanceEntry
가 생성됩니다.
지연된 이미지와 글꼴 외에도 페이지는 새 콘텐츠가 제공될 때 DOM에 새 요소를 추가할 수 있습니다. 이러한 새 요소 중 이전의 가장 큰 콘텐츠 요소보다 큰 요소가 있으면 새 PerformanceEntry
도 보고됩니다.
가장 큰 콘텐츠가 표시 영역이나 DOM에서 삭제되는 경우, 더 큰 요소가 렌더링되지 않는 한 이 요소는 가장 큰 콘텐츠 요소가 됩니다.
사용자가 탭, 스크롤 또는 키 누르기를 통해 페이지와 상호작용하는 즉시 브라우저는 새 항목 보고를 중지합니다. 사용자 상호작용으로 인해 사용자에게 표시되는 내용이 자주 변경되기 때문입니다(특히 스크롤의 경우).
분석을 위해 가장 최근에 전달된 PerformanceEntry
만 분석 서비스에 보고해야 합니다.
로드 시간과 렌더링 시간 비교
보안상의 이유로 이미지의 렌더링 타임스탬프는 Timing-Allow-Origin
헤더가 없는 교차 출처 이미지에 노출되지 않습니다. 대신 로드 시간만 노출됩니다 (다른 많은 웹 API를 통해 이미 노출되어 있기 때문).
이로 인해 웹 API에서 LCP를 FCP보다 먼저 보고하는 것이 불가능해 보이는 상황이 발생할 수 있습니다. 실제로는 그렇지 않지만 보안 제한으로 인해 그렇게 표시되는 것입니다.
가능하면 항상 Timing-Allow-Origin
헤더를 설정하여 측정항목의 정확성을 높이는 것이 좋습니다.
요소 레이아웃 및 크기 변경은 어떻게 처리되나요?
새로운 성능 항목을 계산하고 전달하는 성능 오버헤드를 낮게 유지하기 위해 요소의 크기나 위치를 변경해도 새 LCP 후보가 생성되지 않습니다. 표시 영역에서 요소의 초기 크기와 위치만 고려됩니다.
즉, 처음에는 화면 밖에서 렌더링되었다가 화면 안으로 전환되는 이미지는 보고되지 않을 수 있습니다. 또한 뷰포트에서 처음 렌더링된 후 아래로 푸시되어 시야에서 벗어난 요소도 초기 뷰포트 내 크기를 계속 보고합니다.
예
다음은 최대 콘텐츠 페인트가 일부 인기 웹사이트에서 발생하는 몇 가지 예입니다.
위의 두 타임라인 모두 콘텐츠가 로드되면 가장 큰 요소가 변경됩니다. 첫 번째 예에서는 DOM에 새 콘텐츠가 추가되어 가장 큰 요소를 변경합니다. 두 번째 예에서는 레이아웃이 변경되고 이전에 가장 컸던 콘텐츠가 표시 영역에서 삭제됩니다.
늦게 로드되는 콘텐츠가 페이지에 이미 있는 콘텐츠보다 큰 경우가 많지만 반드시 그런 것은 아닙니다. 다음 두 예는 페이지가 완전히 로드되기 전에 발생하는 LCP를 보여줍니다.
첫 번째 예에서 Instagram 로고는 비교적 일찍 로드되어 다른 콘텐츠가 점진적으로 표시되더라도 가장 큰 요소로 유지됩니다. Google 검색 결과 페이지 예시에서 가장 큰 요소는 이미지나 로고가 로드되기 전에 표시되는 텍스트 단락입니다. 모든 개별 이미지가 이 단락보다 작으므로 로드 프로세스 전반에서 가장 큰 요소로 남아 있습니다.
LCP 측정 방법
LCP는 실험실이나 현장에서 측정할 수 있으며 다음 도구에서 사용할 수 있습니다.
현장 도구
- Chrome 사용자 경험 보고서
- PageSpeed Insights
- Search Console(Core Web Vitals 보고서)
web-vitals
JavaScript 라이브러리
실습 도구
JavaScript에서 LCP 측정
JavaScript에서 LCP를 측정하려면 최대 콘텐츠 페인트 API를 사용하면 됩니다. 다음 예는 largest-contentful-paint
항목을 수신 대기하고 콘솔에 기록하는 PerformanceObserver
를 만드는 방법을 보여줍니다.
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
위 예에서 로깅된 각 largest-contentful-paint
항목은 현재 LCP 후보를 나타냅니다. 일반적으로 내보낸 마지막 항목의 startTime
값은 LCP 값이지만 항상 그런 것은 아닙니다. 일부 largest-contentful-paint
항목은 LCP 측정에 유효하지 않습니다.
다음 섹션에서는 API가 보고하는 내용과 측정항목이 계산되는 방식의 차이점을 보여줍니다.
측정항목과 API의 차이점
- API는 백그라운드 탭에 로드된 페이지의
largest-contentful-paint
항목을 전달하지만 LCP를 계산할 때 이러한 페이지는 무시해야 합니다. - 페이지가 백그라운드로 전환된 후에도 API는 계속
largest-contentful-paint
항목을 전달하지만 LCP를 계산할 때는 이러한 항목을 무시해야 합니다(요소는 페이지가 전 시간 동안 포그라운드에 있었던 경우에만 고려될 수 있음). - 페이지가 뒤로/앞으로 캐시에서 복원되면 API는
largest-contentful-paint
항목을 보고하지 않지만, 사용자가 이를 별도의 페이지 방문으로 인식하므로 이러한 경우 LCP를 측정해야 합니다. - API는 iframe 내의 요소를 고려하지 않지만 측정항목은 페이지의 사용자 환경의 일부이므로 고려합니다. iframe 내에 LCP가 있는 페이지(예: 삽입된 동영상의 포스터 이미지)에서는 CrUX와 RUM 간에 차이가 표시됩니다. LCP를 올바르게 측정하려면 LCP를 고려해야 합니다. 하위 프레임은 API를 사용하여 집계를 위해
largest-contentful-paint
항목을 상위 프레임에 보고할 수 있습니다. - API는 탐색 시작부터 LCP를 측정하지만 사전 렌더링된 페이지의 경우 사용자가 경험한 LCP 시간에 해당하므로
activationStart
부터 LCP를 측정해야 합니다.
개발자는 이러한 미묘한 차이점을 모두 기억하는 대신 web-vitals
JavaScript 라이브러리를 사용하여 LCP를 측정할 수 있습니다. 이 라이브러리는 가능한 경우 이러한 차이점을 자동으로 처리합니다(iframe 문제는 다루지 않음).
import {onLCP} from 'web-vitals';
// Measure and log LCP as soon as it's available.
onLCP(console.log);
JavaScript에서 LCP를 측정하는 방법의 전체 예는 onLCP()
의 소스 코드를 참고하세요.
가장 큰 요소가 가장 중요하지 않은 경우는 어떻게 하나요?
페이지에서 가장 중요한 요소가 가장 큰 요소와 같지 않은 경우도 있으며, 개발자는 이러한 다른 요소의 렌더링 시간을 측정하는 데 더 관심이 있을 수 있습니다. 이 작업은 맞춤 측정항목에 대한 도움말에 설명된 대로 Element Timing API를 사용하여 가능합니다.
LCP를 개선하는 방법
LCP 최적화에 관한 전체 가이드를 통해 현장에서 LCP 타이밍을 파악하고 실험실 데이터를 사용하여 드릴다운하고 최적화하는 과정을 안내받을 수 있습니다.
추가 리소스
- performance.now()의 애니 설리번이 작성한 Chrome의 성능 모니터링에서 얻은 교훈(2019)
변경 로그
측정항목을 측정하는 데 사용되는 API에서 버그가 발견되는 경우가 있으며, 측정항목 자체의 정의에서 버그가 발견되는 경우도 있습니다. 따라서 변경이 필요할 때가 있으며 이러한 변경사항은 내부 보고서 및 대시보드에 개선 또는 회귀로 표시될 수 있습니다.
이를 관리하는 데 도움이 되도록 이러한 측정항목의 구현 또는 정의에 대한 모든 변경사항이 이 변경 로그에 표시됩니다.
이러한 측정항목에 관한 의견이 있으면 web-vitals-feedback Google 그룹에 제출해 주세요.