최대 콘텐츠풀 페인트 최적화
메인 콘텐츠를 더 빠르게 렌더링하는 방법.
쓸 만한 콘텐츠가 보이지 않아요! 로드하는 데 왜 이렇게 오래 걸리는 거죠? 😖
열악한 사용자 경험에 큰 영향을 미치는 요소 중 하나는 사용자가 화면에 렌더링되는 콘텐츠를 보기까지 얼마나 많은 시간이 걸리느냐입니다. First Contentful Paint(최초 콘텐츠풀 페인트, FCP)는 초기 DOM 콘텐츠가 렌더링되는 데 걸리는 시간을 측정하지만 페이지에서 가장 크고 일반적으로 가장 의미 있는 콘텐츠를 렌더링하는 데 걸린 시간은 포착하지 않습니다.
Large Contentful Paint(최대 콘텐츠풀 페인트, LCP)는 Core Web Vitals 메트릭이며 뷰포트에서 가장 큰 콘텐츠 요소가 표시되는 시점을 측정합니다. 페이지의 메인 콘텐츠가 화면에서 렌더링을 완료한 시점을 결정하는 데 사용할 수 있습니다.
열악한 LCP 값이 발생하는 일반적인 이유는 다음과 같습니다.
느린 서버 응답 시간 #
브라우저가 서버에서 콘텐츠를 수신하는 데 시간이 오래 걸릴수록 화면에 무엇이든 렌더링하는 데 더 오래 걸립니다. 더 빠른 서버 응답 시간은 LCP를 포함한 모든 단일 페이지 로드 메트릭을 직접적으로 향상시킵니다.
가장 먼저 해야 할 것은 서버에서 콘텐츠를 처리하는 방법과 위치를 개선하는 것입니다. Time to First Byte(최초 바이트까지의 시간, TTFB)로 서버 응답 시간을 측정하세요. 다음과 같이 다양한 방식으로 TTFB를 개선할 수 있습니다.
- 서버 최적화
- 사용자를 가까운 CDN으로 라우팅
- 자산 캐시
- HTML 페이지 캐시 우선 제공
- 조기에 타사 연결 구축
- 서명된 교환 사용
서버 최적화 #
서버가 완료하는 데 상당한 시간이 걸리는 쿼리를 실행하고 있나요? 아니면 서버 측에서 진행되는 다른 복잡한 작업으로 인해 페이지 콘텐츠를 반환하는 프로세스를 지연되나요? 서버 측 코드의 효율성을 분석하고 개선하면 브라우저가 데이터를 수신하는 데 걸리는 시간이 빨라집니다.
대부분의 서버 측 웹 프레임워크는 브라우저에서 요청할 때 고정 페이지를 즉시 제공하는 것이 아니라, 웹 페이지를 동적으로 생성해야 합니다. 즉, 브라우저가 요청할 때 이미 준비된 완전한 HTML 파일을 보내는 것이 아니라 프레임워크에서 페이지를 구성하는 논리를 실행해야 한다는 의미입니다. 이는 데이터베이스 쿼리의 결과가 보류 중이거나 구성 요소가 UI 프레임워크(예: React )에 의해 마크업으로 생성되어야 하기 때문일 수 있습니다. 서버에서 실행되는 여러 가지 웹 프레임워크에는 이 프로세스의 속도를 높이는 데 사용할 수 있는 성능 지침이 있습니다.
사용자를 가까운 CDN으로 라우팅 #
콘텐츠 전송 네트워크(CDN)은 여러 위치에 분산된 서버 네트워크입니다. 웹 페이지의 콘텐츠가 단일 서버에서 호스팅되는 경우 지리적으로 멀리 떨어져 있는 사용자에게는 해당 웹사이트가 느리게 표시될 수 있습니다. 브라우저 요청이 말 그대로 세계를 한 바퀴 돌아야 하기 때문이죠. 사용자가 멀리 떨어진 서버에 대한 네트워크 요청을 기다리지 않도록 하려면 CDN을 사용하는 것이 좋습니다.
자산 캐시 #
HTML이 고정적이며 모든 요청에 대해 변경할 필요가 없는 경우 캐싱을 통해 HTML이 불필요하게 다시 생성되는 것을 방지할 수 있습니다. 서버 측 캐싱은 생성된 HTML의 복사본을 디스크에 저장해 TTFB를 줄이고 리소스 사용을 최소화할 수 있습니다.
도구 체인에 따라 서버 캐싱을 적용하는 다양한 방법이 있습니다.
- 캐시된 콘텐츠를 제공하거나 애플리케이션 서버 앞에 설치된 경우 캐시 서버 역할을 하도록 역방향 프록시(Varnish , nginx) 구성
- 클라우드 공급자(Firebase, AWS, Azure)의 캐시 동작 구성 및 관리
- 콘텐츠가 캐시되어 사용자에게 더 가까운 곳에 저장되도록 엣지 서버를 제공하는 CDN 사용
HTML 페이지 캐시 우선 제공 #
설치되면 서비스 작업자가 브라우저 백그라운드에서 실행되고 서버의 요청을 가로챌 수 있습니다. 이 수준의 프로그래밍 방식 캐시 제어를 사용하면 HTML 페이지 콘텐츠의 일부 또는 전체를 캐시하고 콘텐츠가 변경된 경우에만 캐시를 업데이트할 수 있습니다.
다음 차트는 이 패턴을 사용하여 사이트에서 LCP 분포가 어떻게 감소했는지 보여줍니다.


이 차트는 지난 28일 동안 단일 사이트의 LCP 분포를 서비스 작업자 상태별로 분류하여 보여줍니다. 캐시 우선 HTML 페이지 서비스가 서비스 작업자(차트의 파란색 부분)에 도입된 후 얼마나 많은 페이지 로드가 더 빨라진 LCP 값을 갖게 됐는지 확인하세요.
조기에 타사 연결 구축 #
타사 출처에 대한 서버 요청은 특히 페이지에 중요한 콘텐츠를 표시해야 하는 경우 LCP에 영향을 줄 수 있습니다. rel="preconnect"
를 사용하여 페이지가 최대한 빨리 연결을 구축할 것임을 브라우저에 알립니다.
<link rel="preconnect" href="https://example.com" />
dns-prefetch
를 사용하여 DNS 조회를 더 빠르게 해결할 수도 있습니다.
<link rel="dns-prefetch" href="https://example.com" />
두 힌트 모두 다르게 작동하지만 preconnect
를 지원하지 않는 브라우저에 대한 대체로 dns-prefetch
를 사용할 수도 있습니다.
<head>
…
<link rel="preconnect" href="https://example.com" />
<link rel="dns-prefetch" href="https://example.com" />
</head>
서명된 교환(SXG) 사용 #
서명된 교환(SXG)이란 쉽게 캐시할 수 있는 형식으로 콘텐츠를 제공하여 더 빠른 사용자 경험을 가능하게 하는 전달 메커니즘입니다. 특히 Google 검색은 SXG를 캐시하고 때로는 미리 가져옵니다. Google 검색에서 트래픽의 많은 부분을 수신하는 사이트의 경우 SXG는 LCP를 개선하기 위한 중요한 도구가 될 수 있습니다. 자세한 내용은 서명된 교환을 참조하세요.
렌더링 차단 JavaScript 및 CSS #
브라우저에서 콘텐츠를 렌더링하려면 먼저 HTML 마크업을 DOM 트리로 구문 분석해야 합니다. HTML 파서는 외부 스타일시트( <link rel="stylesheet">
) 또는 동기 JavaScript 태그( <script src="main.js">
)를 만나면 일시 중지됩니다.
스크립트와 스타일시트는 모두 FCP를 지연시키고 결과적으로 LCP까지 지연시키는 렌더링 차단 리소스입니다. 중요하지 않은 JavaScript 및 CSS를 지연시키면 웹 페이지의 메인 콘텐츠 로드 속도를 빠르게 할 수 있습니다.
CSS 차단 시간 단축 #
다음과 같은 방법으로 필요한 최소 CSS만 사이트에서 렌더링을 차단하도록 합니다.
- CSS 축소
- 중요하지 않은 CSS 지연
- 중요 CSS 즉시 처리
CSS 축소 #
가독성을 높이기 위해 CSS 파일에는 공백, 들여쓰기 또는 주석과 같은 문자를 포함할 수 있습니다. 이런 문자는 모두 브라우저에 필요하지 않으며, 이러한 파일을 축소하면 제거됩니다. 궁극적으로 CSS 차단량을 줄이면 페이지의 메인 콘텐츠를 완전히 렌더링하는 데 걸리는 시간(LCP)이 항상 향상됩니다.
모듈 번들러 또는 빌드 도구를 사용하는 경우 적절한 플러그인을 포함하여 모든 빌드에서 CSS 파일을 축소합니다.
- Webpack: optimize-css-assets-webpack-plugin
- Gulp: gulp-clean-css
- Rollup: rollup-plugin-css-porter

중요하지 않은 CSS 지연 #
Chrome DevTools의 Coverage 탭을 사용하여 웹 페이지에서 사용하지 않는 CSS를 찾습니다.

다음과 같은 방법으로 최적화할 수 있습니다.
- 사용하지 않는 CSS를 완전히 제거하거나 사이트의 별도 페이지에서 사용하는 경우 다른 스타일시트로 이동합니다.
- 초기 렌더링에 필요하지 않은 CSS의 경우 loadCSS를 사용해
rel="preload"
및onload
를 활용해 비동기식으로 파일을 로드합니다.
<link
rel="preload"
href="stylesheet.css"
as="style"
onload="this.rel='stylesheet'"
/>

중요 CSS 즉시 처리 #
스크롤 없이 볼 수 있는 콘텐츠에 사용되는 중요 경로 CSS를 <head>
에 포함해 즉시 처리합니다

중요한 스타일을 즉시 처리하면 중요한 CSS를 가져오기 위해 왕복 요청을 할 필요가 없습니다. 나머지를 연기하면 CSS 차단 시간이 최소화됩니다.
사이트에 즉시 처리 스타일을 수동으로 추가할 수 없다면 라이브러리를 사용해 프로세스를 자동화하세요. 예시는 다음과 같습니다.
- Critical, CriticalCSS 및 Penthouse 는 모두 스크롤 없이 볼 수 있는 CSS를 추출하고 즉시 처리하는 패키지입니다.
- Critters는 중요한 CSS를 즉시 처리하고 나머지는 지연 로드하는 웹팩 플러그인입니다.

JavaScript 차단 시간 단축 #
필요한 최소한의 JavaScript를 다운로드하여 사용자에게 제공합니다. 차단하는 JavaScript의 양을 줄이면 렌더링 속도가 빨라지고 결과적으로 LCP가 향상됩니다.
몇 가지 다른 방법으로 스크립트를 최적화하여 이를 수행할 수 있습니다.
느린 리소스 로드 시간 #
CSS 또는 JavaScript 차단 시간이 증가는 성능 저하에 직접적 영향을 미치지만, 다른 많은 유형의 리소스를 로드하는 데 걸리는 시간도 페인트 시간에 영향을 줄 수 있습니다. LCP에 영향을 미치는 요소 유형은 다음과 같습니다.
<img>
요소<svg>
요소 내부의<image>
<video>
요소(포스터 이미지는 LCP 측정에 사용됨)url()
함수를 통해 로드된 배경 이미지가 있는 요소(CSS 그라데이션과는 대조적임)- 텍스트 노드 또는 기타 인라인 수준 텍스트 요소를 포함하는 블록 수준 요소
스크롤 없이 볼 수 있는 상태로 렌더링된 경우 이러한 요소를 로드하는 데 걸리는 시간은 LCP에 직접적인 영향을 미칩니다. 이러한 파일이 가능한 한 빨리 로드되도록 하는 몇 가지 방법이 있습니다.
- 이미지 최적화 및 압축
- 중요한 리소스 미리 로드
- 텍스트 파일 압축
- 네트워크 연결을 기반으로 다양한 자산 제공(적응형 서비스)
- 서비스 작업자를 사용하여 자산 캐시
이미지 최적화 및 압축 #
대부분의 사이트에서 이미지는 페이지 로드가 완료되었을 때 표시되는 가장 큰 요소입니다. 대표 이미지, 대형 캐러셀 또는 배너 이미지 등이 이러한 예시에 속합니다.

이러한 유형의 이미지를 로드하고 렌더링하는 데 걸리는 시간을 개선하면 LCP 속도 향상에 직접적인 영향을 미칩니다. 이를 달성하는 방법은 다음과 같습니다.
- 처음부터 이미지를 사용하지 않는 것이 좋습니다. 콘텐츠와 관련이 없는 경우 삭제하세요.
- 이미지 압축(예: Imagemin 사용)
- 이미지를 최신 형식(JPEG 2000, JPEG XR 또는 WebP)으로 변환
- 반응형 이미지 사용
- 이미지 CDN 사용 고려
중요한 리소스 미리 로드 #
특정 CSS 또는 JavaScript 파일에서 선언되거나 사용되는 중요한 리소스는 애플리케이션의 수많은 CSS 파일 중 하나에 저장된 글꼴처럼 생각보다 늦게 가져오게 될 수 있습니다.
특정 리소스에 우선순위를 지정해야 하는 경우 <link rel="preload">
를 사용해 더 빨리 가져옵니다. 다양한 유형의 리소스를 미리 로드할 수 있지만 먼저 글꼴, 스크롤 없이 볼 수 있는 이미지 또는 동영상, 중요한 경로 CSS 또는 JavaScript와 같은 중요한 자산을 미리 로드하는 데 집중해야 합니다.
<link rel="preload" as="script" href="script.js" />
<link rel="preload" as="style" href="style.css" />
<link rel="preload" as="image" href="img.png" />
<link rel="preload" as="video" href="vid.webm" type="video/webm" />
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />
Chrome 73부터 반응형 이미지와 함께 사전 로드를 사용해 더 빠른 이미지 로드를 위한 두 가지 패턴을 조합할 수 있습니다.
<link
rel="preload"
as="image"
href="wolf.jpg"
imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
imagesizes="50vw"
/>
텍스트 파일 압축 #
Gzip 및 Brotli와 같은 압축 알고리즘은 서버와 브라우저 사이에 전송되는 텍스트 파일(HTML, CSS, JavaScript)의 크기를 크게 줄일 수 있습니다. Gzip은 모든 브라우저에서 효과적으로 지원되며 더 나은 압축 결과를 제공하는 Brotli 역시 거의 모든 최신 브라우저에서 사용할 수 있습니다.
리소스를 압축하면 전달 크기가 최소화되어 로드 시간이 향상되고 결과적으로 LCP가 향상됩니다.
- 먼저 서버에서 이미 파일을 자동으로 압축하는 것은 아닌지 확인하세요. 대부분의 호스팅 플랫폼, CDN 및 역방향 프록시 서버는 기본적으로 자산을 압축으로 인코딩하거나 쉽게 구성할 수 있도록 지원합니다.
- 파일을 압축하기 위해 서버를 수정해야 하는 경우 gzip 대신 더 나은 압축 비율을 제공하는 Brotli를 사용하는 것이 좋습니다.
- 사용할 압축 알고리즘을 선택한 후에는 브라우저에서 요청하는 즉시 자산을 압축하는 대신 빌드 프로세스 중에 미리 자산을 압축하세요. 이렇게 하면 서버 오버헤드가 최소화되고, 특히 높은 압축률을 사용한 경우 요청 시 지연이 방지됩니다.

적응형 게재 #
페이지의 메인 콘텐츠를 구성하는 리소스를 로드할 때 사용자의 기기나 네트워크 상황에 따라 조건부로 다른 자산을 가져오는 것이 효과적일 수 있습니다. 네트워크 정보, 장치 메모리 및 HardwareConcurrency API를 사용하여 수행할 수 있습니다.
초기 렌더링에 중요한 큰 자산이 있는 경우 사용자의 연결 또는 장치에 따라 동일한 리소스의 다양한 변형을 사용할 수 있습니다. 예를 들어, 4G보다 연결 속도가 낮다면 동영상 대신 이미지를 표시할 수 있습니다.
if (navigator.connection && navigator.connection.effectiveType) {
if (navigator.connection.effectiveType === '4g') {
// Load video
} else {
// Load image
}
}
사용할 수 있는 유용한 속성 목록은 다음과 같습니다.
navigator.connection.effectiveType
: 유효 연결 유형navigator.connection.saveData
: 데이터 절약 활성화/비활성화navigator.hardwareConcurrency
: CPU 코어 수navigator.deviceMemory
: 장치 메모리
서비스 작업자를 사용하여 자산 캐시 #
서비스 작업자는 이 글의 앞에서 언급했듯 더 작은 HTML 응답을 제공하는 것을 포함하여 여러 가지 유용한 작업에 사용될 수 있습니다. 또한 반복 요청 시 네트워크에서 대신 브라우저에 제공할 수 있는 정적 리소스를 캐시하는 데 사용할 수도 있습니다.
서비스 작업자를 사용하여 중요한 리소스를 미리 캐싱하면 로드 시간을 크게 줄일 수 있습니다. 연결 상태가 약한 상태에서, 심지어는 오프라인에서 웹 페이지를 다시 로드하는 사용자의 경우 특히 그렇습니다. Workbox와 같은 라이브러리는 사전 캐시된 자산 업데이트 프로세스를 만들 수 있으며 이는 커스텀 서비스 작업자를 작성하는 것보다 쉽습니다.
클라이언트 측 렌더링 #
많은 사이트에서 클라이언트 측 JavaScript 논리를 사용하여 브라우저에서 직접 페이지를 렌더링합니다. React , Angular 및 Vue 같은 프레임워크 및 라이브러리를 사용하면 웹 페이지의 다양한 측면을 완전히 서버가 아닌 클라이언트에서 처리하는 단일 페이지 애플리케이션을 더 쉽게 구축할 수 있습니다.
대부분 클라이언트에서 렌더링되는 사이트를 구축하는 경우 대규모 JavaScript 번들을 사용한다면 LCP에 미칠 수 있는 영향에 주의해야 합니다. 이를 방지하기 위한 최적화가 이루어지지 않으면 모든 중요한 JavaScript의 다운로드 및 실행이 완료될 때까지 사용자가 페이지의 콘텐츠를 보거나 상호 작용할 수 없습니다.
클라이언트 측 렌더링 사이트를 구축할 때는 다음과 같은 최적화 방식을 고려하세요.
- 중요 JavaScript 최소화
- 서버 측 렌더링 사용
- 사전 렌더링 사용
중요 JavaScript 최소화 #
사이트의 콘텐츠가 일정량의 JavaScript가 다운로드된 후에만 표시되거나 상호 작용할 수 있는 경우 번들의 크기를 최대한 줄여야 합니다. 이를 위해서는 다음과 같은 방법을 사용하세요.
- JavaScript 축소
- 사용하지 않는 JavaScript 지연
- 사용하지 않는 Polyfills 최소화
이러한 최적화에 대해 자세히 알아보려면 JavaScript 차단 시간 단축 섹션으로 돌아가세요.
서버 측 렌더링 사용 #
대부분 클라이언트에서 렌더링되는 사이트의 경우 항상 JavaScript의 양을 최소화하는 것에 가장 먼저 집중해야 합니다. 그러나 최대한 LCP를 개선하기 위해 서버 렌더링 경험을 결합하는 것도 고려해볼 수 있습니다.
이 개념은 서버를 사용하여 애플리케이션을 HTML로 렌더링하는 방식으로 작동합니다. 그러면 클라이언트가 모든 JavaScript 및 필수 데이터를 동일한 DOM 콘텐츠에 "하이드레이션"합니다. 이렇게 하면 페이지의 주요 콘텐츠가 클라이언트에서만이 아니라 서버에서 먼저 렌더링되도록 하여 LCP를 개선할 수 있지만 몇 가지 단점이 있습니다.
- 서버와 클라이언트에서 동일한 JavaScript 렌더링 애플리케이션을 유지 관리하면 복잡성이 증가할 수 있습니다.
- 서버에서 HTML 파일을 렌더링하기 위해 JavaScript를 실행하면 서버에서 정적 페이지를 제공하는 것과 비교하여 항상 서버 응답 시간(TTFB)이 늘어납니다.
- 서버에서 렌더링된 페이지는 상호 작용할 수 있는 것처럼 보이지만 모든 클라이언트 측 JavaScript가 실행될 때까지 사용자 입력에 응답할 수 없습니다. 이로 인해 Time to Interactive(상호 작용까지의 시간, TTI)는 점수가 낮아집니다.
사전 렌더링 사용 #
사전 렌더링은 서버 측 렌더링보다는 덜 복잡하면서도 LCP를 개선하는 방법을 제공하는 별도의 기술입니다. 사용자 인터페이스가 없는 브라우저인 헤드리스 브라우저는 빌드 시 모든 경로의 정적 HTML 파일을 생성하는 데 사용됩니다. 이러한 파일은 이후 애플리케이션에 필요한 JavaScript 번들과 함께 제공될 수 있습니다.
사전 렌더링을 사용하면 TTI에는 여전히 부정적이지만, 서버 응답 시간은 요청된 후에만 각 페이지를 동적으로 렌더링하는 서버 측 렌더링 솔루션의 경우만큼 영향을 받지 않습니다.

개발자 도구 #
LCP를 측정하고 디버그하는 데 사용할 수 있는 여러 가지 도구가 있습니다.
Lighthouse 6.0은 실험실 설정에서 LCP를 측정할 수 있도록 지원합니다.
Chrome DevTools의 Performance 패널에 있는 Timing 섹션에는 LCP 마커가 포함되어 있으며 Related Node 필드 위로 마우스를 가져가면 해당 LCP와 연결된 요소가 표시됩니다.
Chrome User Experience Report 는 원본 수준에서 집계된 실제 LCP 값을 제공합니다.
Philip Walton, Katie Hempenius, Kayce Basques, Ilya Grigorik의 리뷰에 감사드립니다.