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

최대 콘텐츠 렌더링 시간 (LCP)은 세 가지 코어 웹 바이탈 측정항목 중 하나입니다. 이는 웹페이지의 기본 콘텐츠가 얼마나 빠르게 로드되는지, 특히 사용자가 페이지 로드를 시작한 시점부터 표시 영역 내에 가장 큰 이미지 또는 텍스트 블록이 렌더링될 때까지의 시간을 나타냅니다.

우수한 사용자 환경을 제공하려면 최소 75% 의 페이지 방문에서 사이트의 LCP가 2.5초 이하여야 합니다.

올바른 LCP 값은 2.5초 이하이고, 좋지 않은 값은 4.0초보다 큽니다. 그 사이의 값은 개선이 필요합니다.
올바른 LCP 값은 2.5초 이하입니다.

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

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

LCP 측정항목 이해하기

LCP를 최적화하기 전에 개발자는 사이트에 LCP 문제가 있는지, 문제가 있다면 그 정도를 파악해야 합니다.

LCP를 측정할 수 있는 도구는 많지만, 모든 도구가 동일한 방식으로 LCP를 측정하는 것은 아닙니다. 실제 사용자의 LCP 환경을 이해하려면 Lighthouse 또는 로컬 테스트와 같은 실험실 기반 도구뿐만 아니라 실제 사용자가 경험하는 환경을 파악해야 합니다. 이러한 실험실 기반 도구는 LCP를 설명하고 측정항목을 개선하는 데 도움이 되는 풍부한 정보를 제공할 수 있지만 실험실 테스트만으로는 사용자의 경험을 완전히 나타내지는 않습니다.

사이트에 설치된 실제 사용자 모니터링 (RUM) 도구 또는 수백만 개의 웹사이트의 실제 Chrome 사용자로부터 익명 데이터를 수집하는 Chrome 사용자 환경 보고서 (CrUX)를 통해 실제 사용자를 기반으로 LCP 데이터를 표시할 수 있습니다.

PageSpeed Insights에서 CrUX 데이터 사용하기

PageSpeed Insights를 사용하면 실제 사용자의 경험담 섹션에서 CrUX 데이터에 액세스할 수 있습니다. 실습 기반 데이터에 관한 자세한 내용은 성능 문제 진단 섹션을 참고하세요. 가능한 경우 항상 CrUX 데이터에 먼저 집중하십시오.

PageSpeed Insights에 표시된 CrUX 데이터
PageSpeed Insights에 표시된 CrUX 데이터입니다.

CrUX에서 데이터를 제공하지 않는 경우 (예: 페이지 수준 데이터를 가져오기에 트래픽이 부족한 페이지) 페이지에서 실행되는 JavaScript API를 사용하여 수집된 RUM 데이터로 CrUX를 보완할 수 있습니다. 또한 CrUX가 공개 데이터 세트로 노출할 수 있는 것보다 훨씬 많은 데이터를 제공할 수 있습니다. 이 가이드의 뒷부분에서는 자바스크립트를 사용하여 이 데이터를 수집하는 방법을 설명합니다.

LCP 데이터

PageSpeed Insights에서는 최대 4개의 CrUX 데이터 세트를 보여줍니다.

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

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

PageSpeed Insights가 URL 수준 데이터를 사용할 수 없는 원본 수준 데이터로 대체됨
PageSpeed Insights에 URL 수준 데이터가 없으면 출처 수준의 데이터가 표시됩니다.

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

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

추가 측정항목

LCP 최적화 작업을 수행하는 개발자는 LCP에 관한 유용한 정보를 제공할 수 있는 유용한 진단 측정항목인 콘텐츠가 포함된 첫 페인트 (FCP)첫 바이트까지의 시간 (TTFB) 타이밍을 사용할 수도 있습니다.

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

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

페이지 렌더링이 시작되면 초기 페인트 (예: 배경 색상)가 적용된 다음 일부 콘텐츠 (예: 사이트 헤더)가 표시될 수 있습니다. 초기 콘텐츠의 모양은 FCP로 측정되며 FCP와 다른 측정항목의 차이는 매우 중요합니다.

TTFB와 FCP의 큰 차이가 있다면 브라우저에서 렌더링 차단 애셋을 많이 다운로드해야 한다는 의미일 수 있습니다. 이는 브라우저에서 의미 있는 콘텐츠를 렌더링하기 위해 많은 작업을 완료해야 한다는 신호일 수 있으므로 사이트가 클라이언트 측 렌더링에 크게 의존한다는 점을 시사합니다.

FCP와 LCP의 큰 차이는 LCP 리소스가 브라우저에서 우선순위를 지정하는 데 즉시 사용할 수 없거나 (예: 초기 HTML에서 사용할 수 없는 텍스트 또는 이미지) 브라우저가 다른 작업을 완료해야 LCP 콘텐츠를 표시할 수 있다는 의미입니다.

PageSpeed Insights Lighthouse 데이터 사용

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

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

Lighthouse LCP 기회 및 진단
LCP 개선을 위한 Lighthouse 진단 및 권장사항

진단 정보뿐만 아니라 문제를 진단하는 데 도움이 되는 추가 정보를 얻을 수 있는 진단 정보도 확인할 수 있습니다. 최대 콘텐츠 렌더링 시간 요소 진단은 LCP를 구성하는 다양한 타이밍에 관한 유용한 분석을 보여줍니다.

Lighthouse LCP 단계
Lighthouse의 LCP 요소 분석

다음 섹션에서는 LCP의 하위 카테고리를 자세히 살펴봅니다.

LCP 분류

이 섹션에서는 LCP를 가장 중요한 하위 카테고리로 분류하는 방법과 각 하위 카테고리를 최적화하기 위한 구체적인 권장사항 및 권장사항을 설명합니다.

대부분의 페이지 로드에는 일반적으로 여러 네트워크 요청이 포함되지만 LCP 개선 기회를 파악하려면 초기 HTML 문서와 LCP 리소스(해당하는 경우)부터 시작하는 것이 좋습니다.

페이지의 다른 요청이 LCP에 영향을 줄 수 있지만 이 두 요청(특히 LCP 리소스가 시작되고 끝나는 시간)을 통해 페이지가 LCP에 최적화되었는지 여부를 알 수 있습니다.

LCP 리소스를 식별하기 위해 PageSpeed Insights, Chrome DevTools, WebPageTest와 같은 개발자 도구를 사용하여 LCP 요소를 확인할 수 있습니다. 여기에서 페이지에 의해 로드된 모든 리소스의 네트워크 워터폴에서 요소에 의해 로드된 URL (해당하는 경우)과 일치시킬 수 있습니다.

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

HTML 및 LCP 리소스가 강조표시된 네트워크 폭포식 구조
웹페이지 HTML의 로드 시간과 LCP에 필요한 리소스를 보여주는 폭포식 구조 다이어그램

최적화된 페이지의 경우 LCP 리소스 요청이 최대한 빨리 로드되기 시작하고 LCP 리소스의 로드가 완료된 후 LCP 요소가 최대한 빨리 렌더링되도록 하는 것이 좋습니다. 특정 페이지가 이 원칙을 따르는지 시각화하기 위해 총 LCP 시간을 다음과 같은 하위 카테고리로 나눌 수 있습니다.

TTFB (첫 바이트 소요 시간)
사용자가 페이지 로드를 시작한 시점부터 브라우저가 HTML 문서 응답의 첫 번째 바이트를 수신할 때까지의 시간입니다.
리소스 로드 지연
TTFB부터 브라우저가 LCP 리소스 로드를 시작하는 시점 사이의 시간입니다. LCP 요소를 렌더링하는 데 리소스 로드가 필요하지 않은 경우 (예: 요소가 시스템 글꼴로 렌더링된 텍스트 노드인 경우) 이 시간은 0입니다.
리소스 로드 시간
LCP 리소스 자체를 로드하는 데 걸리는 시간입니다. LCP 요소를 렌더링하는 데 리소스 로드가 필요하지 않으면 이 시간은 0입니다.
요소 렌더링 지연
LCP 리소스의 로드가 완료된 시점부터 LCP 요소가 완전히 렌더링될 때까지의 시간입니다.

모든 페이지의 LCP는 다음 4가지 하위 카테고리로 구성됩니다. 이들 간에는 간격이나 중복이 없으며 합산하여 전체 LCP 시간이 됩니다.

4가지 하위 카테고리를 보여주는 LCP 분석
타임라인에 LCP 하위 카테고리 4개가 오버레이된 동일한 폭포식 구조 다이어그램

LCP를 최적화할 때는 이러한 하위 카테고리를 최적화하는 것이 도움이 됩니다. 하지만 일부 최적화에서는 실제로 LCP를 줄이는 것이 아니라 한 부분에서 절약되는 시간을 다른 부분으로 전환하기 때문에 하위 카테고리를 모두 최적화해야 합니다.

예를 들어 네트워크 워터폴 예시에서 이미지를 더 압축하거나 최적의 형식 (예: AVIF 또는 WebP)으로 전환하여 이미지 파일 크기를 줄이면 리소스 로드 시간이 줄어들지만 LCP는 개선되지 않습니다. 이 시간이 요소 렌더링 지연의 일부가 되기 때문입니다. 이는 LCP 요소가 연결된 JavaScript의 로드가 완료된 후 표시될 때까지 숨겨지기 때문입니다.

리소스 로드 시간 하위 카테고리가 단축되었지만 전체 LCP 시간은 동일하게 유지되는 앞서 살펴본 LCP의 동일한 분석입니다.
리소스 로드 시간을 단축하면 LCP를 줄이지 않고 요소 렌더링 지연이 증가합니다.

최적의 하위 카테고리 시간

LCP의 각 하위 카테고리를 최적화하려면 잘 최적화된 페이지에서 이러한 하위 카테고리의 이상적인 분석 내용을 파악해야 합니다.

지연과 관련된 두 하위 카테고리는 최대한 축소되어야 합니다. 다른 두 가지는 네트워크 요청과 관련이 있는데, 이는 기본적으로 시간이 걸리며 완전히 최적화할 수 없습니다.

다음은 이상적인 LCP 분포입니다.

LCP 하위 부분 LCP 비율
TTFB (Time to First byte) ~40%
리소스 로드 지연 <10%
리소스 로드 시간 ~40%
요소 렌더링 지연 <10%
합계 100%

이러한 시간은 가이드라인일 뿐 엄격한 규칙이 아닙니다. 페이지의 LCP 시간이 2.5초 이하인 경우 브레이크다운이 어떻게 나타나는지 크게 중요하지 않습니다. 하지만 지연 카테고리가 불필요하게 길면 2.5초 타겟에 도달하는 데 문제가 발생합니다.

LCP 시간 분류를 다음과 같이 고려하는 것이 좋습니다.

  • LCP 시간의 대부분은 HTML 문서와 LCP 소스를 로드하는 데 필요합니다.
  • LCP 전에 이러한 두 리소스 중 하나가 로드되지 않는 경우 개선할 수 있습니다.

각 카테고리를 최적화하는 방법

이제 LCP 하위 카테고리 시간이 잘 최적화된 페이지에서 어떻게 표시되는지 알았으므로 페이지를 최적화할 수 있습니다.

다음 섹션에는 가장 큰 영향을 미칠 수 있는 최적화부터 시작하여 각 카테고리를 최적화하는 권장사항과 권장사항이 나와 있습니다.

리소스 로드 지연 제거

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

LCP 리소스가 페이지가 로드되는 첫 번째 리소스와 동시에 시작되도록 하는 것이 좋습니다.

첫 번째 리소스 이후 시작되는 LCP 리소스를 보여주는 네트워크 폭포식 구조 다이어그램으로 개선의 여지가 있음
이 페이지에서는 LCP 리소스가 먼저 로드된 스타일시트 직후에 로드되기 시작합니다. 이 부분에는 더 개선의 여지가 있습니다.

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

  • 리소스가 검색되는 시점
  • 리소스에 지정된 우선순위입니다.

리소스 검색 시점 최적화

LCP 리소스가 최대한 빨리 로드되기 시작하려면 브라우저의 미리 로드된 스캐너가 초기 HTML 문서 응답에서 해당 리소스를 검색할 수 있어야 합니다. 검색 가능한 LCP 리소스의 예는 다음과 같습니다.

  • 초기 HTML 마크업에 src 또는 srcset 속성이 있는 <img> 요소
  • CSS 배경 이미지가 필요한 모든 요소. 단, HTML 마크업의 <link rel="preload">가 이미지를 미리 로드하거나 Link 헤더를 사용하여 미리 로드된 상태여야 합니다.
  • 렌더링하기 위해 웹 글꼴이 필요한 텍스트 노드 (HTML 마크업의 <link rel="preload">에 의해 미리 로드되거나 Link 헤더를 사용하여 글꼴이 미리 로드된 경우)

다음은 HTML 문서 응답을 스캔하여 검색할 수 없는 일부 LCP 리소스입니다. 각각의 경우 브라우저는 스크립트를 실행하거나 스타일 시트를 적용해야 LCP 리소스를 찾아서 로드하기 때문에 네트워크 요청이 완료될 때까지 기다려야 합니다.

  • JavaScript를 사용하여 페이지에 동적으로 추가된 <img>
  • src 또는 srcset 속성 (종종 data-src 또는 data-srcset)을 숨기는 JavaScript 라이브러리를 사용하여 지연 로드되는 모든 요소.
  • CSS 배경 이미지가 필요한 요소

불필요한 리소스 로드 지연을 없애려면 HTML 소스에서 LCP 리소스를 검색할 수 있어야 합니다. 리소스가 외부 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">

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

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

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

지연 로드가 없어도 브라우저는 렌더링 차단 리소스가 아니므로 초기에 높은 우선순위로 이미지를 로드하지 않습니다. 다음과 같이 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 리소스는 첫 번째 리소스와 동시에 시작됩니다.

현재 LCP 리소스가 첫 번째 리소스와 동시에 시작하는 것을 보여주는 네트워크 폭포식 구조 다이어그램
이제 LCP 리소스가 스타일 시트와 동시에 로드되기 시작합니다.

요점: LCP 리소스가 최대한 일찍 로드를 시작하지 못하는 또 다른 이유는 HTML 소스에서 검색 가능하더라도 리소스 로드를 시작하기 전에 브라우저가 연결해야 하는 다른 출처에서 호스팅되는 경우입니다. 가능하면 브라우저에서 기존 연결을 재사용하여 시간을 절약할 수 있도록 HTML 문서 리소스와 동일한 출처에 중요한 리소스를 호스팅하는 것이 좋습니다 (이에 대해서는 나중에 자세히 설명함).

요소 렌더링 지연 제거

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

LCP 요소가 리소스의 로드가 완료된 직후에 렌더링할 수 없는 주된 이유는 다른 이유로 렌더링이 차단되어 있기 때문입니다.

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

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

렌더링 차단 스타일시트 줄이기 또는 인라인

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

LCP 리소스보다 로드 시간이 오래 걸리기 때문에 LCP 요소의 렌더링을 차단하는 대용량 CSS 파일을 보여주는 네트워크 워터폴 다이어그램
이미지와 스타일 시트가 동시에 로드되기 시작하지만 스타일 시트가 준비될 때까지는 이미지를 렌더링할 수 없습니다.

이 문제를 해결하려면 다음 중 하나를 수행합니다.

  • 추가 네트워크 요청을 피하기 위해 스타일시트를 HTML에 인라인으로 추가합니다.
  • 스타일 시트의 크기를 줄입니다.

스타일 시트를 인라인으로 추가하는 것은 스타일 시트가 작은 경우에만 LCP를 줄이는 데 효과적입니다. 그러나 스타일 시트가 LCP 리소스보다 로드하는 데 시간이 오래 걸리면 인라인으로 사용하기에는 너무 클 수 있으므로 다음과 같이 스타일 시트의 복잡성을 줄이는 것이 좋습니다.

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

async 또는 defer 속성을 사용하여 페이지의 모든 스크립트를 비동기식으로 설정하는 것이 좋습니다. 동기식 스크립트를 사용하면 거의 항상 성능이 저하됩니다.

하지만 페이지 로드 시 최대한 빨리 자바스크립트를 실행해야 하는 경우 작은 스크립트를 인라인하여 브라우저가 네트워크 요청을 기다리는 데 소비하는 시간을 줄여 LCP를 개선할 수 있습니다.

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

서버 측 렌더링 사용

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

SSR은 다음과 같은 방법으로 LCP를 최적화하는 데 도움이 됩니다.

  • 이렇게 하면 리소스 로드 지연 제거에 설명된 대로 HTML 소스에서 리소스를 검색할 수 있습니다.
  • 페이지를 렌더링하기 전에 완료하기 위해 추가 자바스크립트 요청이 필요하지 않습니다.

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

성능 향상을 위해 주문형 대신 빌드 단계에서 HTML 페이지를 생성하는 것도 좋습니다. 이러한 방식을 정적 사이트 생성(SSG) 또는 사전 렌더링이라고 합니다.

장기 작업 분할

이 권장사항을 모두 따르고 JavaScript 코드가 렌더링을 차단하지 않거나 요소 렌더링을 담당하지 않더라도 LCP가 지연될 수 있습니다.

가장 일반적인 이유는 페이지가 대용량 자바스크립트 파일을 로드할 때 브라우저가 기본 스레드에서 코드를 파싱하고 실행하는 데 시간이 오래 걸리기 때문입니다. 즉, LCP 리소스가 완전히 다운로드되더라도 관련 없는 스크립트 실행이 완료될 때까지 렌더링을 기다려야 할 수 있습니다.

모든 브라우저는 기본 스레드에서 이미지를 렌더링합니다. 즉, 기본 스레드를 차단하면 불필요한 요소 렌더링 지연이 발생할 수 있습니다. 따라서 대용량 자바스크립트 파일을 각각 필요에 따라 파싱할 수 있는 여러 스크립트 파일로 분할하는 것이 좋습니다.

리소스 로드 시간 감소

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

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

리소스 크기 줄이기

LCP 리소스는 일반적으로 이미지 또는 웹 글꼴입니다. 다음 가이드에서는 둘 다 크기를 줄이는 방법을 자세히 설명합니다.

리소스가 이동해야 하는 거리 감소

또한 가능한 한 사용자와 지리적으로 가까운 곳에 서버를 배치하면 로드 시간을 줄일 수 있습니다. 가장 좋은 방법은 콘텐츠 전송 네트워크 (CDN)를 사용하는 것입니다.

실제로 이미지 CDN은 특히 앞서 언급한 전략에 따라 리소스가 이동해야 하는 거리를 줄이고 리소스의 크기를 줄이기 때문에 특히 유용합니다.

핵심 사항: 이미지 CDN은 리소스 로드 시간을 줄이는 좋은 방법입니다. 하지만 서드 파티 도메인을 사용하여 이미지를 호스팅하면 추가 연결 비용이 발생합니다. 출처에 미리 연결하면 이러한 비용 중 일부를 완화할 수 있지만 가장 좋은 방법은 HTML 문서와 동일한 출처의 이미지를 제공하는 것입니다. 이를 위해 많은 CDN을 사용하면 원본에서 원본으로 요청을 프록시할 수 있습니다.

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

페이지에서 동시에 많은 리소스를 로드하는 경우 리소스 하나를 로드하는 데 시간이 오래 걸릴 수 있습니다. 이 문제를 네트워크 경합이라고 합니다.

LCP 리소스에 높은 fetchpriority를 지정하고 최대한 빨리 로드하기 시작하면 브라우저는 우선순위가 낮은 리소스가 리소스와 경쟁하지 않도록 최선을 다합니다. 그러나 한 번에 너무 많은 리소스를 로드해도 LCP에 영향을 미칠 수 있으며, 특히 이러한 리소스 중 많은 수의 fetchpriority가 높은 경우에는 더욱 그렇습니다. fetchpriority가 높은 유일한 리소스만 가장 빠르게 로드해야 하도록 하여 네트워크 경합을 줄이는 것이 좋습니다.

네트워크 시간을 완전히 없앱니다.

리소스 로드 시간을 줄이는 가장 좋은 방법은 프로세스에서 네트워크를 완전히 제거하는 것입니다. 효율적인 캐시 제어 정책으로 리소스를 제공하는 경우, 이러한 리소스를 두 번째로 요청하는 방문자가 캐시에서 리소스를 제공하므로 리소스 로드 시간이 기본적으로 0으로 줄어듭니다.

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

마지막으로 LCP 리소스가 작으면 리소스를 데이터 URI로 인라인하여 추가 네트워크 요청을 제거하는 것이 좋을 수 있습니다. 하지만 데이터 URI를 사용하면 단점이 있습니다. 즉, 리소스가 캐시되지 않으며 추가 디코딩 비용으로 인해 경우에 따라 렌더링 지연이 길어질 수 있습니다.

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

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

다른 방법으로 빠른 사이트의 TTFB가 느린 일반적인 원인은 방문자가 광고나 짧은 링크 등 여러 리디렉션을 통해 도착하기 때문입니다. 항상 방문자가 기다려야 하는 리디렉션 수를 최소화합니다.

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

TTFB를 줄이는 방법은 TTFB 최적화를 참조하세요.

자바스크립트에서 LCP 분류 모니터링

모든 LCP 하위 카테고리의 타이밍 정보는 다음 성능 API의 조합을 통해 JavaScript에서 사용할 수 있습니다.

자바스크립트에서 이러한 타이밍 값을 계산하면 이 값을 분석 제공업체에 전송하거나 개발자 도구에 로깅하여 디버깅 및 최적화에 도움이 됩니다. 예를 들어 다음 스크린샷에서는 User Timing APIperformance.measure() 메서드를 사용하여 Chrome DevTools Performance 패널의 Timings 트랙에 막대를 추가합니다.

Chrome DevTools에 시각화된 LCP 하위 카테고리의 User Timing은
타이밍 트랙에는 LCP 하위 카테고리의 타임라인이 표시됩니다.

시간 트랙의 시각화는 특히 네트워크 및 기본 스레드 트랙과 함께 유용하므로 이러한 기간 동안 페이지에서 어떤 일이 일어나고 있는지 확인할 수 있습니다.

또한 JavaScript를 사용하여 각 하위 카테고리가 차지하는 총 LCP 시간 비율을 계산하여 페이지가 권장 비율 분류를 충족하는지 확인할 수 있습니다.

이 스크린샷은 각 LCP 하위 카테고리의 총 시간과 총 LCP 시간 비율을 콘솔에 로깅하는 예를 보여줍니다.

LCP 하위 카테고리 시간과 LCP 비율이 콘솔에 출력됨
LCP 하위 카테고리 타이밍 및 백분율

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

const LCP_SUB_PARTS = [
  'Time to first byte',
  'Resource load delay',
  'Resource load time',
  '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 분석을 더 잘 이해할 수 있도록 수정할 수 있습니다.

웹 바이탈 확장 프로그램을 사용하여 LCP 분류 모니터링

웹 바이탈 확장 프로그램콘솔 로깅에 LCP 시간, LCP 요소, 4가지 하위 카테고리를 로깅하여 이 분석을 표시합니다.

LCP 하위 파트 타이밍을 보여주는 웹 바이탈 확장 프로그램의 콘솔 로깅 스크린샷
웹 바이탈 확장 프로그램의 콘솔 패널에 LCP 분석이 표시됩니다.