최대 콘텐츠 렌더링 시간 최적화

LCP를 분석하고 개선할 주요 영역을 파악하는 방법에 관한 단계별 안내입니다.

최대 콘텐츠 페인트 (LCP)는 세 가지 코어 웹 바이탈 측정항목 중 하나이며 웹페이지의 기본 콘텐츠가 로드되는 속도를 나타냅니다. 구체적으로 LCP는 사용자가 페이지 로드를 시작한 시점부터 표시 영역 내에서 가장 큰 이미지나 텍스트 블록이 렌더링될 때까지의 시간을 측정합니다.

우수한 사용자 환경을 제공하기 위해 사이트는 페이지 방문의 75% 이상에서 LCP를 2.5초 이하로 유지해야 합니다.

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder"></ph> <ph type="x-smartling-placeholder"></ph> 양호한 LCP 값은 2.5초 이하이고, 낮은 값은 4.0초보다 크며, 그 사이의 모든 값은 개선이 필요합니다.
적절한 LCP 값은 2.5초 이하입니다.

다양한 요인이 브라우저가 웹페이지를 로드하고 렌더링하는 속도에 영향을 줄 수 있으며, 이러한 요인의 지연은 LCP에 상당한 영향을 미칠 수 있습니다.

페이지의 한 부분을 빠르게 수정해도 LCP가 유의미하게 개선되는 경우는 드뭅니다. LCP를 개선하려면 전체 로드 프로세스를 살펴보고 모든 단계가 최적화되었는지 확인해야 합니다.

LCP 측정항목 이해하기

LCP를 최적화하기 전에 개발자는 LCP 문제인지 여부와 그 정도를 파악해야 합니다.

LCP는 여러 도구로 측정할 수 있지만 모두 동일한 방식으로 LCP를 측정하는 것은 아닙니다. 실제 사용자의 LCP를 이해하려면 Lighthouse나 현지 테스트와 같은 실험실 기반 도구가 아닌 실제 사용자의 경험을 살펴봐야 합니다. 실험실 기반 도구를 사용하면 LCP를 개선하는 데 도움이 되는 풍부한 정보를 얻을 수 있지만, 실험실 테스트만으로는 실제 사용자 경험을 완전히 반영하지 못할 수 있습니다.

실제 사용자에 기반한 LCP 데이터는 사이트에 설치된 RUM (Real User Monitoring) 도구를 사용하거나 수백만 개의 웹사이트의 실제 Chrome 사용자로부터 익명 데이터를 수집하는 Chrome 사용자 환경 보고서 (CrUX)를 통해 확인할 수 있습니다.

PageSpeed Insights CrUX LCP 데이터 사용하기

PageSpeed Insights의 상단 섹션에서는 실제 사용자가 경험하는 사항 살펴보기라는 CrUX 데이터에 액세스할 수 있습니다. 보다 자세한 실습 기반 데이터는 성능 문제 진단이라고 표시된 하단 섹션에서 확인할 수 있습니다. 웹사이트에 대한 CrUX 데이터를 사용할 수 있는 경우 항상 실제 사용자 데이터에 초점을 맞추세요.

<ph type="x-smartling-placeholder">
</ph> PageSpeed Insights에 표시된 CrUX 데이터 <ph type="x-smartling-placeholder">
</ph> PageSpeed Insights에 표시된 CrUX 데이터
를 통해 개인정보처리방침을 정의할 수 있습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다. <ph type="x-smartling-placeholder">

PageSpeed Insights에서는 최대 네 가지 CrUX 데이터를 표시합니다.

  • 이 URL모바일 데이터
  • 이 URL데스크톱 데이터
  • 전체 Origin모바일 데이터
  • 전체 Origin데스크톱 데이터

이 섹션의 상단과 오른쪽 상단에 있는 컨트롤에서 전환 버튼을 사용할 수 있습니다. URL에 URL 수준에서 표시하기에 충분한 데이터가 없지만 출처에 관한 데이터는 있는 경우 PageSpeed Insights에서는 항상 출처 데이터를 표시합니다.

<ph type="x-smartling-placeholder">
</ph> URL 수준 데이터를 사용할 수 없는 출처 수준 데이터로 돌아가는 PageSpeed 통계 <ph type="x-smartling-placeholder">
</ph> PageSpeed Insights에 URL 수준의 데이터가 없으면 출처 수준 데이터입니다.

전체 출처의 LCP는 해당 페이지에서 LCP가 로드되는 방식에 따라 해당 출처의 다른 페이지와 비교하여 개별 페이지의 LCP와 크게 다를 수 있습니다. 또한 방문자가 해당 페이지로 이동하는 방식에 따라서도 영향을 받을 수 있습니다. 홈페이지는 신규 사용자가 방문하는 경향이 있으므로 캐시된 콘텐츠가 없는 '콜드' 로드가 될 수 있으며 웹사이트에서 가장 느린 페이지인 경우가 많습니다.

CrUX 데이터의 네 가지 카테고리를 살펴보면 LCP 문제가 이 페이지에만 해당하는지 아니면 보다 일반적인 사이트 전체 문제가 발생하는지 이해하는 데 도움이 됩니다. 마찬가지로 LCP 문제가 있는 기기 유형을 표시할 수 있습니다.

PageSpeed Insights CrUX 보조 측정항목 사용하기

LCP를 최적화하려는 경우 콘텐츠가 포함된 첫 페인트 (FCP)첫 바이트까지의 시간 (TTFB) 타이밍도 사용해야 합니다. 이는 LCP에 관한 유용한 정보를 제공할 수 있는 유용한 진단 측정항목입니다.

TTFB는 방문자가 페이지로 이동하기 시작하는 시간 (예: 링크 클릭)이며 HTML 문서의 첫 번째 바이트가 수신될 때까지의 시간입니다. TTFB가 높으면 2.5초의 LCP를 달성하기 어렵거나 심지어 불가능할 수도 있습니다.

TTFB가 높은 것은 여러 개의 서버 리디렉션, 방문자가 가장 가까운 사이트 서버에서 멀리 떨어져 있는 경우, 방문자가 네트워크 상태가 좋지 않거나, 쿼리 매개변수로 인해 캐시된 콘텐츠를 사용할 수 없기 때문에 발생할 수 있습니다.

페이지 렌더링이 시작되면 초기 페인트 (예: 배경 색상)가 진행되고 이후 일부 콘텐츠 (예: 사이트 헤더)가 표시될 수 있습니다. 초기 콘텐츠의 모양은 FCP에 의해 측정됩니다. FCP와 다른 측정항목 간의 델타는 매우 의미 있는 정보를 제공합니다.

TTFB와 FCP 사이의 큰 델타는 브라우저가 렌더링 차단 애셋을 많이 다운로드해야 함을 나타낼 수 있습니다. 또한 의미 있는 콘텐츠를 렌더링하려면 많은 작업을 완료해야 함을 의미할 수 있습니다. 이는 클라이언트 측 렌더링에 크게 의존하는 사이트의 전형적인 신호입니다.

FCP와 LCP 사이의 델타가 큰 경우 브라우저에서 LCP 리소스를 즉시 사용할 수 없거나 (예: 초기 HTML에서 사용할 수 없는 대신 JavaScript에 의해 관리되는 텍스트 또는 이미지) 브라우저가 LCP 콘텐츠를 표시하기 전에 다른 작업을 완료하고 있음을 나타냅니다.

PageSpeed Insights Lighthouse 데이터 사용하기

PageSpeed Insights의 Lighthouse 섹션에서 LCP 개선을 위한 안내를 제공하지만 먼저 주어진 LCP가 CrUX에서 제공하는 실제 사용자 데이터와 광범위하게 일치하는지 확인해야 합니다. Lighthouse와 CrUX의 의견이 일치하지 않는다면 CrUX가 사용자 환경을 더 정확하게 파악할 가능성이 높습니다. 조치를 취하기 전에 CrUX 데이터가 전체 출처가 아닌 페이지에 관한 것인지 확인하세요.

Lighthouse와 CrUX 모두에서 개선이 필요한 LCP 값이 표시되는 경우 Lighthouse 섹션에서 LCP 개선 방법에 관한 중요한 지침을 제공할 수 있습니다. LCP 필터를 사용하면 다음과 같이 LCP와 관련된 감사만 표시할 수 있습니다.

<ph type="x-smartling-placeholder">
</ph> Lighthouse LCP 기회 및 진단 <ph type="x-smartling-placeholder">
</ph> Lighthouse 진단 및 LCP 개선을 위한 제안입니다.

개선을 위한 추천뿐 아니라 진단 정보를 통해 문제를 진단하는 데 도움이 되는 추가 정보를 얻을 수 있습니다. 콘텐츠가 포함된 최대 페인트 요소 진단에는 LCP를 구성하는 다양한 타이밍에 관한 유용한 분석이 표시됩니다.

<ph type="x-smartling-placeholder">
</ph> Lighthouse의 LCP 단계 <ph type="x-smartling-placeholder">
</ph> Lighthouse의 LCP 요소 분석

이 하위 부분에 대해서는 다음 단계에서 자세히 살펴보겠습니다.

LCP 분류

PageSpeed Insights로도 이 측정항목을 개선하는 방법을 알 수 없을 때 LCP를 최적화하는 작업이 더 복잡할 수 있습니다. 복잡한 작업은 일반적으로 더 작고 관리하기 쉬운 작업으로 나누어서 개별적으로 처리하는 것이 좋습니다.

이 섹션에서는 LCP를 가장 중요한 하위 부분으로 나눈 다음 각 부분을 최적화하는 방법에 관한 구체적인 권장사항 및 권장사항을 제시하는 방법을 설명합니다.

대부분의 페이지 로드에는 일반적으로 여러 개의 네트워크 요청이 포함되지만 LCP를 개선할 기회를 파악하기 위해 먼저 다음 두 가지만 확인해야 합니다.

  1. 초기 HTML 문서
  2. LCP 리소스 (해당하는 경우)

페이지의 다른 요청은 LCP에 영향을 미칠 수 있지만, 이 두 요청, 특히 LCP 리소스가 시작되고 끝나는 시간을 보면 페이지가 LCP에 최적화되었는지 알 수 있습니다.

LCP 리소스를 식별하려면 개발자 도구 (예: 앞서 설명한 PageSpeed Insights, Chrome DevTools 또는 WebPageTest)를 사용하여 LCP 요소를 확인하면 됩니다. 여기에서 페이지에 의해 로드된 모든 리소스의 네트워크 폭포식 구조에 있는 요소에 의해 로드된 URL (해당하는 경우 다시 한 번)과 일치시킬 수 있습니다.

예를 들어 다음 시각화는 LCP 요소를 렌더링하기 위해 이미지 요청이 필요한 일반적인 페이지 로드의 네트워크 폭포식 구조 다이어그램에서 이러한 리소스가 강조 표시된 것을 보여줍니다.

<ph type="x-smartling-placeholder">
</ph> HTML 및 LCP 리소스가 강조 표시된 네트워크 폭포식 구조 <ph type="x-smartling-placeholder">
</ph> 다음 각 인스턴스의 로드 시간을 보여주는 폭포 다이어그램 웹페이지의 HTML과 LCP에 필요한 리소스로 구성됩니다.

최적화된 페이지의 경우 LCP 리소스 요청이 가능한 한 빨리 로드되기 시작하고 LCP 리소스 로드가 완료된 후 LCP 요소가 가능한 한 빠르게 렌더링되기를 원합니다. 특정 페이지가 이 원칙을 준수하는지 여부를 시각화하기 위해 총 LCP 시간을 다음과 같은 하위 부분으로 분류할 수 있습니다.

첫 바이트까지의 시간 (TTFB)
사용자가 페이지 로드를 시작한 시점부터 브라우저가 실행될 때까지의 시간입니다. HTML 문서 응답의 첫 바이트를 수신합니다.
리소스 로드 지연
TTFB와 브라우저가 LCP 리소스 로드를 시작하는 시점 사이의 시간입니다. 만약 LCP 요소는 렌더링하기 위해 리소스 로드가 필요하지 않습니다 (예: 요소는 시스템 글꼴로 렌더링된 텍스트 노드), 이번에는 0입니다.
리소스 로드 시간
LCP 리소스 자체를 로드하는 데 걸리는 시간입니다. LCP가 요소는 렌더링하기 위해 리소스 로드가 필요하지 않으며, 이 시간은 0입니다.
요소 렌더링 지연
LCP 리소스 로드가 완료된 시점과 LCP 요소 사이의 시간입니다. 있습니다.

모든 페이지의 LCP는 다음 4가지 하위 카테고리로 구성됩니다. 격차나 중복이 없습니다. 합쳐서 전체 LCP 시간이 됩니다.

<ph type="x-smartling-placeholder">
</ph> 4가지 하위 카테고리를 보여주는 LCP 분석 <ph type="x-smartling-placeholder">
</ph> LCP 하위 카테고리 4개가 오버레이된 동일한 폭포 다이어그램 확인할 수 있습니다.

모든 페이지의 LCP 값은 아래와 같은 4개의 하위 부분으로 나눌 수 있습니다. 그 사이에 겹치는 부분이나 차이가 없습니다. 이러한 시간의 총합은 전체 LCP 시간이 됩니다.

LCP를 최적화할 때 이러한 하위 부분을 개별적으로 최적화하는 것이 좋습니다. 그러나 모든 잠재고객을 최적화해야 한다는 사실을 명심하는 것도 중요합니다. 경우에 따라 한 부분에 최적화를 적용해도 LCP가 개선되지 않고 절약된 시간이 다른 부분으로 옮겨집니다.

예를 들어 이전의 네트워크 폭포식 구조에서 이미지를 더 많이 압축하거나 보다 최적의 형식 (예: AVIF 또는 WebP)으로 전환하여 이미지의 파일 크기를 줄이면 리소스 로드 시간은 줄어들지만 시간이 요소 렌더링 지연 하위 부분으로 이동하므로 실제로는 LCP를 개선하지 못합니다.

<ph type="x-smartling-placeholder">
</ph> 앞서 살펴본 동일한 LCP 분석에서 리소스 로드 기간 하위 카테고리가 단축되지만 전체 LCP 시간은 동일하게 유지됩니다. <ph type="x-smartling-placeholder">
</ph> 리소스 로드 시간을 줄이면 LCP를 줄이지 않고 요소 렌더링 지연이 증가합니다.

이 문제가 발생하는 이유는 이 페이지에서 JavaScript 코드의 로드가 완료될 때까지 LCP 요소가 숨겨지고 이후 모든 항목이 한 번에 표시되기 때문입니다.

이 예에서는 최상의 LCP 결과를 얻기 위해 이러한 모든 하위 부분을 최적화해야 한다는 점을 설명합니다.

최적의 서브파트 시간

LCP의 각 하위 부분을 최적화하려면 최적화된 페이지에서 이러한 하위 부분의 이상적인 분석이 무엇인지 이해하는 것이 중요합니다.

네 개의 하위 부분 중 두 부분에는 '지연'이라는 단어가 있습니다. 넣으세요. 이는 이 시간을 가능한 한 0에 가깝게 하고 싶다는 단서입니다. 나머지 두 부분은 네트워크 요청을 포함하는데, 특성상 이 작업은 시간이 걸립니다.

LCP 하위 부분 LCP 비율
첫 바이트까지의 시간 ~40%
리소스 로드 지연 10% 미만
리소스 로드 시간 ~40%
요소 렌더링 지연 10% 미만
합계 100%

이러한 시간 분류는 엄격한 규칙이 아닌 가이드라인입니다. 페이지의 LCP 시간이 일관되게 2.5초 이내라면 상대적 비율은 중요하지 않습니다. 그러나 '지연' 상황이 발생할 때 지속적으로 2.5초 타겟을 달성하기가 매우 어렵습니다.

LCP 시간의 상세 분석은 다음과 같습니다.

  • LCP 시간의 대부분은 HTML 문서 및 LCP 소스를 로드하는 데 사용되어야 합니다.
  • 이 두 리소스 중 하나가 로드되지 않는 LCP 이전에는 언제든 개선 기회가 됩니다.
를 통해 개인정보처리방침을 정의할 수 있습니다. <ph type="x-smartling-placeholder">

각 부분을 최적화하는 방법

이제 각 LCP 하위 부분 시간을 잘 최적화된 페이지에서 분석하는 방법을 이해했으니 내 페이지를 최적화할 수 있습니다.

다음 4개 섹션에서는 각 부분을 최적화하는 방법에 대한 추천과 권장사항을 제공합니다. 가장 큰 영향을 미칠 가능성이 높은 최적화부터 순서대로 제시되어 있습니다.

1. 리소스 로드 지연 제거

이 단계의 목표는 LCP 리소스가 최대한 빨리 로드되도록 하는 것입니다. 이론적으로는 리소스 로드가 TTFB 직후에 시작될 수 있지만, 실제로는 브라우저가 실제로 리소스 로드를 시작하기까지 항상 약간의 지연이 발생합니다.

일반적으로 LCP 리소스는 해당 페이지에서 로드하는 첫 번째 리소스와 동시에 로드되기 시작해야 합니다. 달리 말하면, LCP 리소스가 첫 번째 리소스보다 늦게 로드되기 시작하면 개선의 여지가 있습니다.

<ph type="x-smartling-placeholder">
</ph> 첫 번째 리소스 이후에 시작되는 LCP 리소스를 보여주는 네트워크 폭포식 구조 다이어그램으로 개선의 여지가 있음 <ph type="x-smartling-placeholder">
</ph> 이 페이지에서는 LCP 리소스가 스타일이 지정된 후 시트가 있습니다. 여기에 개선의 여지가 있습니다.

일반적으로 LCP 리소스의 로드 속도에 영향을 주는 두 가지 요소가 있습니다.

  • 리소스가 검색될 때
  • 리소스에 부여되는 우선순위

리소스가 검색될 때 최적화

LCP 리소스가 최대한 빨리 로드되도록 하려면 브라우저의 미리 로드 스캐너가 초기 HTML 문서 응답에서 리소스를 검색할 수 있어야 합니다. 예를 들어 다음과 같은 경우 브라우저가 HTML 문서 응답을 스캔하여 LCP 리소스를 검색할 수 있습니다.

  • LCP 요소는 <img> 요소이며 src 또는 srcset 속성이 초기 HTML 마크업에 있습니다.
  • LCP 요소에는 CSS 배경 이미지가 필요하지만 이 이미지는 HTML 마크업의 <link rel="preload"> 또는 Link 헤더를 사용하여 미리 로드됩니다.
  • LCP 요소는 렌더링하기 위해 웹 글꼴이 필요한 텍스트 노드이며, 글꼴은 HTML 마크업의 <link rel="preload"> 또는 Link 헤더를 사용하여 로드됩니다.

다음은 HTML 문서 응답을 스캔할 때 LCP 리소스를 찾을 수 없는 몇 가지 예입니다.

  • LCP 요소는 자바스크립트를 사용하여 페이지에 동적으로 추가되는 <img>입니다.
  • LCP 요소는 src 또는 srcset 속성 (보통 data-src 또는 data-srcset)을 숨기는 JavaScript 라이브러리와 함께 지연 로드됩니다.
  • LCP 요소에는 CSS 배경 이미지가 필요합니다.

이러한 각 경우에 브라우저는 스크립트를 실행하거나 스타일시트를 적용해야 LCP 리소스를 검색하고 로드를 시작할 수 있습니다(일반적으로 네트워크 요청이 완료되기를 기다려야 하는 작업이 포함). 이는 최적이 아닙니다.

불필요한 리소스 로드 지연을 제거하려면 LCP 리소스를 HTML 소스에서 검색할 수 있어야 합니다. 리소스가 외부 CSS 또는 JavaScript 파일에서만 참조되는 경우 LCP 리소스를 높은 가져오기 우선순위로 미리 로드해야 합니다. 예를 들면 다음과 같습니다.

<!-- Load the stylesheet that will reference the LCP image. -->
<link rel="stylesheet" href="/path/to/styles.css">

<!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. -->
<link rel="preload" fetchpriority="high" as="image" href="/path/to/hero-image.webp" type="image/webp">
<ph type="x-smartling-placeholder">

리소스에 부여된 우선순위 최적화

LCP 리소스를 HTML 마크업에서 검색할 수 있더라도 여전히 첫 번째 리소스만큼 빨리 로드되지 않을 수 있습니다. 이는 브라우저 미리 로드 스캐너의 우선순위 휴리스틱이 리소스가 중요하다는 것을 인식하지 못하거나 다른 리소스가 더 중요하다고 판단하는 경우 발생할 수 있습니다.

예를 들어 <img> 요소에 loading="lazy"를 설정하면 HTML을 사용하여 LCP 이미지를 지연시킬 수 있습니다. 지연 로드를 사용하면 레이아웃에서 이미지가 표시 영역에 있음을 확인할 때까지 리소스가 로드되지 않으므로 다른 경우보다 늦게 로드가 시작될 수 있습니다.

<ph type="x-smartling-placeholder">

지연 로드가 없어도 이미지는 렌더링 차단 리소스가 아니므로 처음에 브라우저에서 가장 높은 우선순위로 로드되지 않습니다. 더 높은 우선순위의 이점을 누릴 수 있는 리소스에 fetchpriority 속성을 사용하여 어떤 리소스가 가장 중요한지 브라우저에 알려줄 수 있습니다.

<img fetchpriority="high" src="/path/to/hero-image.webp">

페이지의 LCP 요소일 가능성이 높다면 <img> 요소에 fetchpriority="high"를 설정하는 것이 좋습니다. 그러나 둘 이상의 이미지에 높은 우선순위를 설정하면 LCP를 줄이는 데 도움이 되지 않습니다.

또한 문서 응답 초기에 시작했을 때 표시되지 않는 캐러셀 슬라이드의 이미지와 같이 스타일 때문에 표시되지 않을 수 있는 이미지의 우선순위를 낮출 수도 있습니다.

<img fetchpriority="low" src="/path/to/carousel-slide-3.webp">

특정 리소스의 우선순위를 낮추면 대역폭이 더 필요한 리소스에 더 많은 대역폭을 사용할 수 있지만 주의하세요. 항상 DevTools에서 리소스 우선순위를 확인하고 실험실 및 필드 도구로 변경사항을 테스트하세요.

LCP 리소스 우선순위와 검색 시간을 최적화한 후에는 네트워크 폭포식 구조가 다음과 같이 표시됩니다 (LCP 리소스가 첫 번째 리소스와 동시에 시작됨).

<ph type="x-smartling-placeholder">
</ph> 이제 LCP 리소스가 첫 번째 리소스와 동시에 시작되는 것을 보여주는 네트워크 폭포식 구조 다이어그램 <ph type="x-smartling-placeholder">
</ph> 이제 LCP 리소스가 스타일 시트와 동시에 로드되기 시작합니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다. <ph type="x-smartling-placeholder">

2. 요소 렌더링 지연 제거

이 단계의 목표는 리소스 로드가 완료된 후 LCP 요소가 즉시 렌더링될 수 있도록 하는 것입니다.

LCP 요소가 리소스 로드가 완료된 후 즉시 렌더링할 수 없는 주된 이유는 다른 이유로 렌더링이 차단된 경우입니다.

  • 아직 로드 중인 <head>의 스타일시트 또는 동기 스크립트로 인해 전체 페이지의 렌더링이 차단되었습니다.
  • LCP 리소스 로드가 완료되었지만 LCP 요소가 아직 DOM에 추가되지 않았습니다 (일부 JavaScript 코드가 로드되기를 기다리고 있음).
  • 사용자가 참여해야 하는 실험을 결정하는 A/B 테스트 라이브러리와 같은 다른 코드에 의해 요소가 숨겨지고 있습니다.
  • 긴 작업으로 인해 기본 스레드가 차단되며 렌더링 작업은 이러한 장기 작업이 완료될 때까지 기다려야 합니다.

다음 섹션에서는 불필요한 요소 렌더링 지연의 가장 일반적인 원인을 해결하는 방법을 설명합니다.

렌더링 차단 스타일시트를 축소하거나 인라인으로 변환하기

HTML 마크업에서 로드된 스타일 시트는 그 뒤에 오는 모든 콘텐츠의 렌더링을 차단합니다. 이는 일반적으로 스타일이 지정되지 않은 HTML을 렌더링하지 않기 때문입니다. 그러나 스타일 시트가 너무 커서 LCP 리소스보다 로드하는 데 시간이 훨씬 더 오래 걸리면 다음 예와 같이 리소스 로드가 완료된 후에도 LCP 요소가 렌더링되지 않습니다.

<ph type="x-smartling-placeholder">
</ph> LCP 리소스보다 로드하는 데 시간이 오래 걸려 LCP 요소의 렌더링을 차단하는 대형 CSS 파일을 보여주는 네트워크 폭포식 구조 다이어그램 <ph type="x-smartling-placeholder">
</ph> 이미지와 스타일 시트가 동시에 로드되기 시작하지만 스타일 시트가 준비될 때까지 이미지를 렌더링할 수 없습니다.

이 문제를 해결하려면 다음 중 한 가지 방법을 사용할 수 있습니다.

  • 추가적인 네트워크 요청을 피하려면 스타일 시트를 HTML에 인라인으로 추가합니다. 또는
  • 줄일 수 있습니다

일반적으로 HTML의 인라인 콘텐츠는 후속 페이지 로드 시 캐싱의 이점을 누릴 수 없으므로 스타일 시트가 작은 경우에만 스타일 시트를 인라인 처리하는 것이 좋습니다. 스타일 시트가 너무 커서 LCP 리소스보다 로드하는 데 시간이 더 오래 걸리면 인라인 처리를 위한 좋은 후보가 아닐 수 있습니다.

대부분의 경우 스타일 시트가 LCP 요소의 렌더링을 차단하지 않도록 하는 가장 좋은 방법은 LCP 리소스보다 작아지도록 크기를 줄이는 것입니다. 이렇게 하면 대부분의 방문에서 병목 현상이 발생하지 않습니다.

다음은 스타일 시트 크기를 줄이기 위한 몇 가지 권장사항입니다.

렌더링 차단 JavaScript 지연 또는 인라인

동기 스크립트 (async 또는 defer 속성이 없는 스크립트)는 페이지의 <head>에 추가할 필요가 거의 없으며, 추가 작업은 거의 항상 성능에 부정적인 영향을 미칩니다.

자바스크립트 코드를 페이지 로드에서 최대한 빨리 실행해야 하는 경우 다른 네트워크 요청을 기다리는 동안 렌더링이 지연되지 않도록 코드를 인라인하는 것이 가장 좋습니다. 하지만 스타일시트와 마찬가지로, 스크립트의 크기가 매우 작은 경우에만 삽입해야 합니다.

금지사항
<head>
  <script src="/path/to/main.js"></script>
</head>
권장사항
<head>
  <script>
    // Inline script contents directly in the HTML.
    // IMPORTANT: only do this for very small scripts.
  </script>
</head>

서버 측 렌더링 사용

서버 측 렌더링 (SSR)은 서버에서 클라이언트 측 애플리케이션 로직을 실행하고 전체 HTML 마크업으로 HTML 문서 요청에 응답하는 프로세스입니다.

LCP 최적화의 관점에서 볼 때 SSR에는 다음과 같은 두 가지 주요 이점이 있습니다.

  • 위의 1단계에서 설명한 것처럼 HTML 소스에서 이미지 리소스를 검색할 수 있습니다.
  • 페이지 콘텐츠를 렌더링하기 전에 추가 자바스크립트 요청을 완료할 필요가 없습니다.

SSR의 가장 큰 단점은 서버 처리 시간이 추가로 필요하여 TTFB가 느려질 수 있다는 점입니다. 하지만 서버 처리 시간은 개발자가 제어할 수 있는 반면 사용자의 네트워크 및 기기 기능은 제어할 수 없기 때문에 이러한 단점은 그만한 가치가 있습니다.

SSR과 유사한 옵션을 정적 사이트 생성 (SSG) 또는 사전 렌더링이라고 합니다. 이는 주문형이 아니라 빌드 단계에서 HTML 페이지를 생성하는 프로세스입니다. 아키텍처에서 사전 렌더링이 가능한 경우 일반적으로 성능을 위해 사전 렌더링을 선택하는 것이 더 좋습니다.

장기 작업 분할

이전 권장사항을 따랐고 자바스크립트 코드가 렌더링을 차단하지 않고 요소 렌더링을 담당하지 않더라도 여전히 LCP를 지연시킬 수 있습니다.

이 문제가 발생하는 가장 일반적인 이유는 페이지에서 브라우저의 기본 스레드에서 파싱하고 실행해야 하는 대용량 자바스크립트 파일을 로드할 때 발생합니다. 즉, 이미지 리소스가 완전히 다운로드되었더라도 관련 없는 스크립트가 실행을 완료할 때까지 기다려야 렌더링할 수 있습니다.

오늘날 모든 브라우저는 기본 스레드에서 이미지를 렌더링합니다. 즉, 기본 스레드를 차단하면 불필요한 요소 렌더링 지연이 발생할 수도 있습니다.

3. 리소스 로드 시간 줄이기

이 단계의 목표는 네트워크를 통해 사용자 기기로 리소스 바이트를 전송하는 데 소요되는 시간을 줄이는 것입니다. 일반적으로 다음과 같은 세 가지 방법을 사용할 수 있습니다.

  • 리소스 크기를 줄입니다.
  • 리소스가 이동해야 하는 거리를 줄입니다.
  • 네트워크 대역폭에 대한 경합을 줄입니다.
  • 네트워크 시간을 완전히 없앱니다.

리소스 크기 줄이기

페이지의 LCP 리소스 (있는 경우)는 이미지 또는 웹 글꼴입니다. 다음 가이드에서는 이 두 가지 크기를 줄이는 방법을 자세히 설명합니다.

리소스가 이동해야 하는 거리 줄이기

리소스의 크기를 줄일 뿐만 아니라 서버를 가능한 한 지리적으로 사용자와 가깝게 배치하여 로드 시간을 줄일 수도 있습니다. 이를 위한 가장 좋은 방법은 콘텐츠 전송 네트워크 (CDN)를 사용하는 것입니다.

특히 이미지 CDN은 리소스가 이동해야 하는 거리를 줄일 뿐만 아니라 일반적으로 리소스의 크기를 줄여 모든 크기 축소 권장사항을 자동으로 구현하므로 특히 유용합니다.

<ph type="x-smartling-placeholder">

네트워크 대역폭 경합 줄이기

리소스의 크기와 이동 거리를 줄였더라도 다른 많은 리소스를 동시에 로드하면 리소스를 로드하는 데 시간이 오래 걸릴 수 있습니다. 이 문제를 네트워크 경합이라고 합니다.

LCP 리소스에 높은 fetchpriority을 지정하고 최대한 빨리 로드를 시작한 경우 브라우저는 우선순위가 낮은 리소스가 경쟁하지 못하도록 최선을 다합니다. 그러나 fetchpriority가 높은 많은 리소스를 로드하거나 일반적으로 많은 리소스를 로드하는 경우 LCP 리소스 로드 속도에 영향을 줄 수 있습니다.

네트워크 시간을 완전히 제거

리소스 로드 시간을 줄이는 최선의 방법은 프로세스에서 네트워크를 완전히 제거하는 것입니다. 효율적인 캐시 제어 정책으로 리소스를 제공하면 해당 리소스를 다시 요청하는 방문자가 캐시에서 리소스를 제공하게 되므로 리소스 로드 기간이 기본적으로 0이 됩니다.

LCP 리소스가 웹 글꼴인 경우 웹 글꼴 크기를 줄이는 것 외에도 웹 글꼴 리소스 로드 시 렌더링을 차단해야 하는지 여부도 고려해야 합니다. auto 또는 block 이외의 값으로 font-display 값을 설정하면 로드 중에 텍스트가 항상 표시되고 추가 네트워크 요청 시 LCP가 차단되지 않습니다.

마지막으로 LCP 리소스가 작은 경우 리소스를 데이터 URL로 인라인 처리하는 것이 타당할 수 있으며, 이렇게 하면 추가 네트워크 요청도 사라집니다. 하지만 데이터 URL을 사용할 때는 주의할 점이 있습니다. 리소스를 캐시할 수 없고 경우에 따라 추가 디코딩 비용으로 인해 렌더링 지연이 길어질 수 있기 때문입니다.

4. 첫 바이트 소요 시간 단축

이 단계의 목표는 초기 HTML을 가능한 한 빨리 제공하는 것입니다. 이 단계는 개발자가 가장 적게 제어할 수 있는 단계가 되는 경우가 많기 때문에 마지막으로 나열됩니다. 그러나 이후의 모든 단계에 직접 영향을 미치므로 가장 중요한 단계 중 하나이기도 합니다. 백엔드가 콘텐츠의 첫 바이트를 전달할 때까지 프런트엔드에서는 아무 일도 일어나지 않습니다. 따라서 TTFB의 속도를 높이기 위해 할 수 있는 일은 다른 모든 로드 측정항목도 개선됩니다.

속도가 빠른 사이트에서 TTFB가 느려지는 일반적인 원인 중 하나는 방문자가 광고나 짧은 링크 등을 통해 여러 번 리디렉션되는 경우입니다. 방문자가 기다려야 하는 리디렉션 수를 항상 최소화합니다.

또 다른 일반적인 원인은 CDN 에지 서버에서 캐시된 콘텐츠를 사용할 수 없고 모든 요청을 원본 서버로 다시 전달해야 하는 경우입니다. 방문자가 고유한 URL 매개변수를 사용하여 분석을 수행하는 경우, 다른 페이지로 연결되지 않더라도 이러한 상황이 발생할 수 있습니다.

TTFB 최적화에 관한 구체적인 안내는 TTFB 최적화 가이드를 참고하세요.

JavaScript에서 LCP 분류 모니터링

앞에서 설명한 모든 LCP 하위 부분의 타이밍 정보는 다음 성능 API를 조합하여 JavaScript에서 사용할 수 있습니다.

JavaScript에서 이러한 타이밍 값을 계산하면 분석 제공업체로 전송하거나 개발자 도구에 로깅하여 디버깅 및 최적화에 도움이 된다는 이점이 있습니다.

예를 들어 다음 스크린샷에서는 User Timing APIperformance.measure() 메서드를 사용하여 Chrome DevTools Performance 패널의 Timings 트랙에 막대를 추가합니다.

<ph type="x-smartling-placeholder">
</ph> Chrome DevTools에 시각화된 LCP 하위 카테고리의 사용자 시간 측정값 <ph type="x-smartling-placeholder">
</ph> 타이밍 트랙에는 LCP 하위 카테고리의 타임라인이 표시됩니다.

타이밍 트랙의 시각화는 네트워크기본 스레드 트랙과 함께 살펴볼 때 특히 유용합니다. 이 기간 동안 페이지에서 일어나는 다른 일을 한눈에 확인할 수 있기 때문입니다.

타이밍 트랙에서 LCP 하위 부분을 시각화하는 것 외에도 자바스크립트를 사용하여 총 LCP 시간에서 각 하위 부분이 차지하는 비율을 계산할 수도 있습니다. 이 정보를 통해 페이지가 앞서 설명한 권장 비율 분류를 충족하는지 확인할 수 있습니다.

이 스크린샷은 각 LCP 하위 부분의 총 시간과 콘솔에 발생한 총 LCP 시간 비율을 기록하는 예를 보여줍니다.

<ph type="x-smartling-placeholder">
</ph> 콘솔에 출력되는 LCP 하위 카테고리 시간 및 LCP 비율 <ph type="x-smartling-placeholder">
</ph> LCP 하위 카테고리 타이밍 및 비율

이 두 시각화는 모두 다음 코드로 생성되었습니다.

const LCP_SUB_PARTS = [
  'Time to first byte',
  'Resource load delay',
  'Resource load duration',
  'Element render delay',
];

new PerformanceObserver((list) => {
  const lcpEntry = list.getEntries().at(-1);
  const navEntry = performance.getEntriesByType('navigation')[0];
  const lcpResEntry = performance
    .getEntriesByType('resource')
    .filter((e) => e.name === lcpEntry.url)[0];

  // Ignore LCP entries that aren't images to reduce DevTools noise.
  // Comment this line out if you want to include text entries.
  if (!lcpEntry.url) return;

  // Compute the start and end times of each LCP sub-part.
  // WARNING! If your LCP resource is loaded cross-origin, make sure to add
  // the `Timing-Allow-Origin` (TAO) header to get the most accurate results.
  const ttfb = navEntry.responseStart;
  const lcpRequestStart = Math.max(
    ttfb,
    // Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
  );
  const lcpResponseEnd = Math.max(
    lcpRequestStart,
    lcpResEntry ? lcpResEntry.responseEnd : 0
  );
  const lcpRenderTime = Math.max(
    lcpResponseEnd,
    // Use LCP startTime (the final LCP time) because there are sometimes
    // slight differences between loadTime/renderTime and startTime
    // due to rounding precision.
    lcpEntry ? lcpEntry.startTime : 0
  );

  // Clear previous measures before making new ones.
  // Note: due to a bug, this doesn't work in Chrome DevTools.
  LCP_SUB_PARTS.forEach((part) => performance.clearMeasures(part));

  // Create measures for each LCP sub-part for easier
  // visualization in the Chrome DevTools Performance panel.
  const lcpSubPartMeasures = [
    performance.measure(LCP_SUB_PARTS[0], {
      start: 0,
      end: ttfb,
    }),
    performance.measure(LCP_SUB_PARTS[1], {
      start: ttfb,
      end: lcpRequestStart,
    }),
    performance.measure(LCP_SUB_PARTS[2], {
      start: lcpRequestStart,
      end: lcpResponseEnd,
    }),
    performance.measure(LCP_SUB_PARTS[3], {
      start: lcpResponseEnd,
      end: lcpRenderTime,
    }),
  ];

  // Log helpful debug information to the console.
  console.log('LCP value: ', lcpRenderTime);
  console.log('LCP element: ', lcpEntry.element, lcpEntry.url);
  console.table(
    lcpSubPartMeasures.map((measure) => ({
      'LCP sub-part': measure.name,
      'Time (ms)': measure.duration,
      '% of LCP': `${
        Math.round((1000 * measure.duration) / lcpRenderTime) / 10
      }%`,
    }))
  );
}).observe({type: 'largest-contentful-paint', buffered: true});

이 코드를 로컬 디버깅에 있는 그대로 사용하거나 수정해 이 데이터를 분석 서비스 제공업체에 전송하여 실제 사용자를 위한 페이지에서 LCP 분석 내용을 더 잘 이해할 수 있습니다.

Web Vitals 확장 프로그램을 사용하여 LCP 분류를 모니터링합니다.

웹 바이탈 확장 프로그램LCP 시간, LCP 요소, 이러한 네 가지 하위 부분을 콘솔에 기록하여 이러한 분석을 확인할 수 있도록 합니다.

<ph type="x-smartling-placeholder">
</ph> LCP 하위 부분 타이밍을 보여주는 Web Vitals 확장 프로그램의 콘솔 로깅 <ph type="x-smartling-placeholder">
</ph> 웹용 콘솔 패널 vitals 확장 프로그램에는 LCP 분석이 표시됩니다.

요약

LCP는 복잡하며 시기는 여러 요인의 영향을 받을 수 있습니다. 하지만 LCP 최적화가 주로 LCP 리소스의 부하를 최적화하는 것이라면 작업을 크게 간소화할 수 있습니다.

LCP 최적화는 크게 네 단계로 요약할 수 있습니다.

  1. LCP 리소스가 최대한 빨리 로드되기 시작하도록 합니다.
  2. LCP 요소가 리소스 로드가 완료되는 즉시 렌더링할 수 있는지 확인합니다.
  3. 품질 저하 없이 LCP 리소스의 로드 시간을 최대한 줄입니다.
  4. 초기 HTML 문서를 최대한 빠르게 게재합니다.

페이지에서 이러한 단계를 따를 수 있다면 사용자에게 최적의 로드 환경을 제공하고 있다고 확신할 수 있으며, 이 결과가 실제 LCP 점수에도 반영되어 있음을 확인할 수 있을 것입니다.