LCP Element Render Delay 최적화
다운로드에서 화면 표시까지: Largest Contentful Paint의 element render delay 부분을 개선하는 방법을 알아보세요.

이 가이드는 Core Web Vitals 리소스 센터의 Largest Contentful Paint (LCP) 섹션의 일부입니다. Element Render Delay는 LCP 타임라인의 마지막 단계로, LCP 리소스 다운로드가 완료된 시점과 화면에 시각적으로 페인트되는 시점 사이의 간격을 나타냅니다.
LCP Element Render Delay 최적화
LCP의 4단계 중 Element Render Delay는 가장 오해받는 단계입니다. 팀들은 TTFB를 최적화하고, Resource Load Delay를 제거하며, 에셋을 압축하여 Resource Load Duration을 단축합니다. 그들은 네트워크 워터폴이 끝나는 것을 보고 작업이 완료되었다고 가정합니다. 하지만 그들은 틀렸습니다.
Element Render Delay는 LCP 리소스의 다운로드가 완료된 시점부터 사용자의 화면에 요소가 완전히 페인트될 때까지의 시간입니다. 이것은 네트워크 문제가 아니라 main-thread 문제입니다. render delay가 높다는 것은 브라우저가 이미지나 폰트를 가지고 있지만, 다른 작업으로 너무 바빠서 실제로 그리지 못하고 있음을 의미합니다. 이 지연은 모든 네트워크 요청이 완료된 후에도 때로는 200ms 이상을 추가하여 LCP 점수에 직접적인 악영향을 미칩니다.
정확한 정의: 마지막 구간(Last Mile) 문제
Element Render Delay는 LCP 리소스(예: 이미지 파일 또는 웹 폰트)의 마지막 바이트가 브라우저에 도착하는 순간 시작됩니다. 그리고 LCP 요소가 화면에 시각적으로 페인트될 때 끝납니다. 말 그대로 가장 마지막 단계입니다.
시스템 폰트를 사용하는 텍스트 기반 LCP 요소의 경우, 외부 리소스가 필요하지 않기 때문에 이 지연은 종종 0입니다. 그러나 LCP 요소가 이미지이거나 사용자 지정 웹 폰트를 사용하는 대다수의 사이트에서 이 단계는 가장 큰 병목 현상인 경우가 많습니다. 브라우저는 다운로드된 비트를 가시적인 픽셀로 변환하는 CPU 바인딩 작업에 이 시간을 소비합니다.
'이유': 꽉 막힌 조립 라인
render delay를 해결하려면 브라우저가 페이지를 그리는 방법을 이해해야 합니다. 이는 Critical Rendering Path라고 불리는 다단계 프로세스입니다. 공장의 조립 라인이라고 생각해 보세요:
- 청사진 구성(DOM 및 CSSOM): 브라우저는 HTML을 파싱하여 Document Object Model(DOM)을 구축하고, CSS를 파싱하여 CSS Object Model(CSSOM)을 구축합니다. 이들은 페이지 콘텐츠와 스타일링을 위한 청사진입니다.
- 청사진 결합(Render Tree): DOM과 CSSOM은 페이지를 렌더링하는 데 필요한 노드만 포함하는 Render Tree로 결합됩니다.
<head>와 같은 요소나display: none;이 적용된 요소는 생략됩니다. - 형태 계산(Layout): 브라우저는 Render Tree의 모든 요소의 정확한 크기와 위치를 계산합니다. 이 단계는 "reflow"라고도 합니다.
- 픽셀 색칠(Paint): 브라우저는 텍스트, 색상, 이미지, 테두리, 그림자 등을 고려하여 각 요소의 픽셀을 채웁니다.
- 레이어 조립(Composite): 페이지는 여러 레이어에 그려지며, 최종 화면 이미지를 생성하기 위해 올바른 순서로 조립됩니다.
Element Render Delay는 이 마지막 단계인 Layout, Paint, Composite에서 소비되는 시간입니다. 이 전체 조립 라인은 단일 작업자인 main thread에 의해 실행됩니다. 해당 작업자가 긴 JavaScript 작업을 실행하거나 거대한 CSS 파일을 파싱하느라 바쁘면 조립 라인이 멈춥니다. LCP 이미지가 도착했을지라도, 처리하고 페인트할 수 있도록 main thread가 자유로워질 때까지 로딩 독에 대기하고 있게 됩니다.
Element Render Delay를 파악하는 방법
이 문제를 진단하는 과정은 엄격한 두 단계를 따릅니다. 첫 번째 단계를 건너뛰지 마십시오.
1단계: 필드 데이터(RUM)로 검증
DevTools를 열기 전에, Element Render Delay가 실제 사용자에게 진짜 문제인지 확인해야 합니다. 제가 만든 CoreDash와 같은 전문가급 Real User Monitoring(RUM) 도구가 필수적입니다. 이는 사이트의 LCP를 4개의 하위 부분으로 분류합니다. RUM 데이터의 75백분위수에서 상당한 Element Render Delay가 나타난다면, 해결해야 할 영향력 있는 검증된 문제가 있는 것입니다.
2단계: DevTools로 진단
RUM이 문제가 있는 페이지를 식별하면, Chrome DevTools의 Performance 패널을 사용하여 원인을 분석하십시오.
- Performance 탭으로 이동하여 "Web Vitals" 체크박스를 활성화합니다.
- "Record and reload page" 버튼을 클릭합니다.
- "Timings" 트랙에서 LCP 마커를 클릭합니다. 아래의 "Summary" 탭은 4개의 LCP 단계 각각에 대한 정확한 duration을 보여줍니다. Element render delay의 값을 기록하세요.
- 이제 타임라인의 Main 트랙을 검토하십시오. LCP 리소스의 네트워크 요청이 끝난 시점과 LCP 타이밍 마커 사이에 발생하는 긴 작업(빨간색 모서리가 있는 노란색 블록)을 찾으십시오. 이러한 작업이 지연의 직접적인 원인입니다. 그 위로 마우스를 가져가 책임이 있는 스크립트를 식별하십시오.
일반적인 원인과 영향력이 큰 해결책
높은 Element Render Delay는 거의 항상 차단된 main thread로 인해 발생합니다.
원인: 렌더링 차단 CSS
문제: 기본적으로 CSS는 렌더링을 차단합니다. 브라우저는 <head>에 연결된 모든 CSS 파일을 다운로드하고 파싱할 때까지 픽셀을 페인트하지 않습니다. 크고 복잡한 스타일시트는 main thread를 수백 밀리초 동안 차지하여 Layout 및 Paint 단계의 시작을 지연시킬 수 있습니다. 사이트가 여러 스타일시트를 로드하고 각각 별도의 네트워크 요청 및 파싱 주기를 요구할 때 이 문제는 더욱 복잡해집니다. CSS payload를 줄이는 방법에 대한 자세한 전략은 사용하지 않는 CSS 제거 가이드를 참조하세요.
해결책: CSS를 작고 깨끗하며 캐시 가능하게 만드십시오.
- 사용하지 않는 CSS 제거: 이것은 영향력이 가장 큰 단일 최적화입니다. 대규모 사이트에서 사용하지 않는 CSS는 전체 스타일시트 크기의 70% 이상을 차지할 수 있습니다. PurgeCSS와 같은 도구는 HTML과 JavaScript를 스캔하여 사용하지 않는 선택자를 식별할 수 있습니다. 데드 룰(dead rule)을 제거하면 다운로드 시간과 main thread의 파싱 시간이 모두 줄어듭니다.
- 작고 캐시 가능한 스타일시트를 목표로 하십시오: CSS 파일의 스위트 스팟은 압축 기준 약 10-15kB입니다. 이보다 작으면 너무 많은 병렬 요청으로 분할될 위험이 있으며, 각 요청에는 자체 연결 오버헤드가 있습니다. 이보다 크면 특히 느린 모바일 네트워크에서 차단 시간이 늘어납니다. 이 범위 내의 잘 구조화된 단일 스타일시트는 빠르게 다운로드되고 빠르게 파싱되며, 재방문을 위해 브라우저에 캐시됩니다.
- 최후의 수단으로만 CSS를 인라인하십시오: 중요 CSS를
<style>블록에 인라인하면 첫 페이지 로드를 위한 네트워크 요청은 제거되지만, 대가가 따릅니다: 인라인된 CSS는 브라우저 캐시를 사용할 수 없습니다. 재방문하는 모든 사용자는 페이지를 방문할 때마다 이를 다시 다운로드합니다. 재방문 사용자가 있는 대부분의 사이트에서는 브라우저가 캐시하는 작은 외부 스타일시트가 더 나은 선택입니다. 인라이닝은 재방문 사용자가 매우 적은 랜딩 페이지에만 적합합니다.
CSS의 영향력 정량화: CSS가 render delay에 얼마나 기여하는지 측정하려면 Chrome DevTools의 Coverage 탭(Ctrl+Shift+P 누른 후 "Coverage" 입력)을 엽니다. 페이지를 로드하고 CSS 파일에서 사용되지 않은 바이트의 비율을 확인하십시오. 사용하지 않는 CSS의 비율이 높다는 것은 정리를 통해 Element Render Delay를 줄일 수 있다는 명확한 신호입니다.
원인: 긴 JavaScript 작업
문제: 이것이 가장 일반적인 원인입니다. 프레임워크, 분석 스크립트, A/B 테스트 도구 또는 제대로 최적화되지 않은 코드 등 무거운 JavaScript 실행은 main thread를 독점할 수 있습니다. 단일의 긴 작업으로 인해 렌더링이 수백 밀리초 동안 차단되어 Element Render Delay가 직접적으로 추가될 수 있습니다. Google은 50ms 이상 걸리는 모든 작업을 long task로 정의하며, 200ms를 초과하는 작업은 치명적으로 긴 것으로 간주합니다. JavaScript 지연 전략의 전체 모음은 JavaScript를 지연하는 14가지 방법에 대한 기사를 참조하십시오.
해결책: 작업을 쪼개십시오.
- Main Thread 양보(Yield): 긴 작업은 더 작은 단위로 쪼개야 합니다.
setTimeout(..., 0)또는 최신scheduler.yield()API를 사용하여 제어권을 주기적으로 브라우저에 다시 양보(yielding)함으로써 이를 수행할 수 있습니다. 이렇게 하면 브라우저가 작업 사이에 렌더링 업데이트를 수행할 수 있습니다. - 서드파티 최적화 및 지연: 모든 서드파티 스크립트를 감사하십시오. 초기 렌더링에 필수가 아니라면
defer속성으로 로드하거나 페이지 로드 후에 주입하십시오. A/B 테스트를 위한 스크립트는 의도적으로 렌더링을 차단하는 경우가 많으므로 특히 문제가 됩니다. - 시각적 업데이트에
requestAnimationFrame사용: JavaScript가 페이지 로드 중에 DOM 조작을 수행해야 하는 경우 해당 작업을requestAnimationFrame으로 래핑하십시오. 이렇게 하면 다음 Paint 직전에 작업이 실행되도록 예약되어, JavaScript 작업 사이에 브라우저가 프레임을 렌더링할 기회를 보장합니다.
DevTools에서 Long Task 식별
Chrome DevTools의 Performance 패널에서 긴 작업은 "Main" 트랙에 오른쪽 상단에 빨간색 삼각형이 있는 노란색 블록으로 나타납니다. 어떤 스크립트에 책임이 있는지 식별하려면:
- Performance 패널에서 페이지 로드를 기록합니다.
- Timings 트랙에서 LCP 마커를 찾습니다.
- LCP 리소스의 네트워크 요청 완료와 LCP 마커 사이에서 발생하는 긴 작업이 있는지 Main 트랙을 검사합니다.
- Summary 패널에서 call stack을 보려면 이러한 작업을 클릭합니다. call stack은 긴 작업의 원인이 되는 소스 파일과 함수를 보여줍니다.
일반적인 서드파티 위반자들
실제 컨설팅 경험에 따르면, Element Render Delay를 유발하는 가장 일반적인 서드파티 스크립트는 다음과 같습니다:
- A/B 테스트 도구 (Optimizely, VWO, AB Tasty): 변형 간의 콘텐츠 깜박임을 방지하기 위해 의도적으로 렌더링을 차단하는 경우가 많습니다. 실험 결정을 서버 측(서버 측 테스트)으로 이동하면 이 문제가 완전히 제거됩니다.
- 동기식 태그가 있는 태그 관리자: 동기식(비동기가 아닌) 태그로 구성된 태그 관리자는 렌더링을 차단하는 스크립트를 주입할 수 있습니다. DOM ready 또는 window load 이후에 시작되도록 모든 태그가 설정되어 있는지 컨테이너를 확인하십시오.
- 동의 관리 플랫폼: 결정이 내려질 때까지 렌더링을 차단하는 쿠키 동의 배너는 LCP를 지연시킬 수 있습니다. Critical Rendering Path를 차단하지 않는 비동기식 구현을 사용하십시오.
- 채팅 위젯: 라이브 채팅 스크립트는 페이지 로드 시 무거운 초기화 코드를 실행하는 경우가 많습니다. 페이지가 상호작용 가능해질 때까지 로딩을 지연시키거나, 사용자 상호작용(예: 클릭) 시 로드하십시오.
원인: 클라이언트 측 렌더링(CSR)
문제: 순수 클라이언트 측 렌더링에서는 초기 HTML에 LCP 요소가 존재하지 않는 경우가 많습니다. JavaScript가 먼저 실행되어 DOM을 빌드하고, LCP 요소를 삽입해야 비로소 브라우저가 이를 렌더링할 수 있습니다. 이 전체 프로세스가 하나의 거대한 render delay입니다.
해결책: 서버에서 렌더링하십시오. 다른 방법은 없습니다. 서버 측 렌더링(SSR) 또는 정적 사이트 생성(SSG)을 사용하여 서버에서 보낸 초기 HTML 문서에 LCP 요소가 있는지 확인하십시오. 이렇게 하면 지연의 원인이 되는 JavaScript 기반 렌더링 단계 전체가 제거됩니다.
원인: 다른 코드에 의해 숨겨진 콘텐츠
문제: 때때로 LCP 요소가 DOM에 있지만 CSS(예: opacity: 0)나 스크립트(예: "스크롤 시 표시" 애니메이션 또는 어떤 변형을 표시할지 결정 중인 A/B 테스트 도구)에 의해 숨겨져 있을 수 있습니다. 요소가 다운로드되어 준비되었지만, 아직 보이지 않기 때문에 페인트할 수 없습니다.
해결책: 즉각적인 가시성을 확보하십시오. LCP 요소의 경우, 진입 애니메이션이나 초기 로드 시 이를 숨기는 어떤 로직도 사용하지 마십시오. 요소는 DOM에 표시되어야 하며, 첫 번째 페인트부터 표시되도록 스타일이 지정되어야 합니다. 비동기적으로 실행되도록 A/B 테스트 도구를 구성하거나 LCP 요소의 가시성에 미치는 영향을 최소화하도록 하십시오.
원인: 과도한 DOM 크기
문제: 큰 DOM(1,500개 이상의 노드)은 모든 렌더링 작업의 비용을 증가시킵니다. 각 레이아웃 계산, 스타일 재계산, 페인트 작업은 더 많은 노드를 처리해야 하므로 main thread에서 더 많은 시간이 소요됩니다. CSS와 JavaScript가 잘 최적화되어 있더라도, 비대해진 DOM은 엄청난 양으로 인해 render delay를 추가합니다. DOM 크기를 줄이는 방법에 대한 자세한 전략은 과도한 DOM 크기 방지 가이드를 참조하십시오.
해결책: 초기 렌더링에 참여하는 DOM 노드의 수를 줄이십시오.
- HTML 구조 단순화: 불필요한 래퍼(wrapper) 요소를 제거합니다. 깊게 중첩된 구조를 평탄화합니다. 레이아웃에 추가적인
<div>요소 대신 CSS Grid 또는 Flexbox를 사용합니다. - 긴 목록 가상화: 수백 개의 목록 항목(제품 그리드, 데이터 테이블)이 있는 페이지의 경우, 뷰포트에 현재 보이는 항목만 렌더링하는 가상화 라이브러리를 사용하십시오.
- 스크롤 아래 콘텐츠의 지연 렌더링:
content-visibility: auto(아래에서 다룸)를 사용하여 화면 밖 섹션의 렌더링을 완전히 건너뜁니다.
고급 전술: 렌더링 완벽 제어
복잡한 애플리케이션은 main thread에 대한 더 많은 통제권이 필요합니다.
content-visibility로 성능 잠금 해제
CSS content-visibility 속성은 큰 페이지를 위해 만들어졌습니다. 스크롤 아래에 있는 페이지 섹션에 content-visibility: auto;를 설정하면, 브라우저에 해당 콘텐츠가 뷰포트에 들어올 때까지 Layout, Paint, Composite 작업을 건너뛸 수 있다고 알려줍니다. 이렇게 하면 초기 렌더링 작업 부하가 줄어들어 main thread가 LCP 요소를 더 빨리 페인트할 수 있습니다.
핵심은 content-visibility: auto를 contain-intrinsic-size와 짝지어 숨겨진 콘텐츠에 대한 placeholder 크기를 제공하는 것입니다. 이것이 없으면 브라우저가 숨겨진 섹션의 높이를 알 수 없기 때문에 스크롤바 동작이 불규칙해집니다.
/* Apply to below-the-fold sections */
.below-fold-section {
content-visibility: auto;
contain-intrinsic-size: auto 500px; /* Estimated height of the section */
}
/* Example: A long article page */
.article-comments {
content-visibility: auto;
contain-intrinsic-size: auto 800px;
}
.related-products {
content-visibility: auto;
contain-intrinsic-size: auto 600px;
}
.site-footer {
content-visibility: auto;
contain-intrinsic-size: auto 300px;
}
성능 영향: Chrome Developers 블로그 게시물에 따르면, 블로그 페이지의 스크롤 아래 섹션에 content-visibility: auto를 적용하면 렌더링 시간이 최대 7배 단축되었습니다. 브라우저는 이러한 섹션에 대한 Layout, Paint, Composite 작업을 완전히 건너뛰어 main thread가 LCP 요소를 포함하여 스크롤 위 콘텐츠에 집중할 수 있도록 해줍니다. 브라우저 지원은 Chromium, Firefox, Safari 18+ 등 모든 최신 브라우저에 적용됩니다.
Web Workers로 작업 오프로드하기
Web Workers를 사용하면 main thread를 완전히 벗어나 백그라운드 스레드에서 JavaScript를 실행할 수 있습니다. Worker에서 실행되는 무거운 계산은 렌더링을 차단할 수 없습니다. 이 사이트인 corewebvitals.io는 분석 처리에 Web Worker를 사용하며, 성능상 이점은 확실합니다. main thread는 중단 없이 페인팅할 수 있도록 자유로운 상태를 유지합니다.
그렇긴 하지만, Web Workers는 대부분의 웹사이트에서 흔한 패턴은 아닙니다. 별도의 JavaScript 파일과 postMessage를 통한 통신이 필요하며, DOM에 접근할 수 없습니다. 대부분의 CMS 플랫폼과 사이트 빌더는 이에 대한 기본 지원을 제공하지 않으므로 커스텀 개발 없이는 구현하기 어렵습니다. 사용할 기술적 능력이 있다면, 이것은 main thread를 깨끗하게 유지하는 가장 효과적인 방법 중 하나입니다. 그러나 대부분의 팀의 경우, 이 페이지의 다른 최적화 방법들이 현실적으로 더 큰 영향을 미칠 것입니다.
// main.js: Create a worker and send data for processing
const worker = new Worker('/js/analytics-worker.js');
// Offload heavy analytics processing to the worker thread
worker.postMessage({
type: 'process-events',
events: collectedEvents
});
// Receive results without blocking the main thread
worker.onmessage = (event) => {
console.log('Analytics processed:', event.data.summary);
};
// analytics-worker.js: Runs in a background thread
self.onmessage = (event) => {
if (event.data.type === 'process-events') {
// Heavy computation happens here, off the main thread
const summary = processEvents(event.data.events);
self.postMessage({ summary });
}
};
실제 사례의 영향
- 사례 1: 렌더링 차단 CSS 병목 현상: DebugBear는 큰 CSS 파일로 인해 눈에 띄는 render delay가 발생한 사이트를 분석했습니다. LCP 이미지는 다운로드되었지만, 브라우저는 CSS를 파싱하느라 멈춰 있었습니다. 중요 CSS를 인라인하는 것만으로 브라우저는 HTML이 파싱된 직후 LCP 요소를 포함한 페이지 콘텐츠를 거의 즉시 페인트할 수 있었고, 스타일시트로 인한 render delay를 효과적으로 제거했습니다.
- 사례 2: A/B 테스트 페널티: 주요 이커머스 사이트에서 동기식 A/B 테스트 스크립트로 인해 LCP가 지연되고 있음을 발견했습니다. LCP 이미지가 빠르게 다운로드되었음에도 불구하고, 어떤 제품 이미지를 표시할지 결정하는 동안 스크립트가 main thread를 차단했습니다. 중요하지 않은 요소에 대해 초기 페이지 로드 후에 A/B 테스트를 실행하도록 옮긴 즉시 LCP가 400ms 이상 개선되었으며, 이 시간은 모두 Element Render Delay에서 복구되었습니다.
체크리스트: Element Render Delay를 제거하는 방법
높은 Element Render Delay는 혼잡한 main thread를 나타냅니다. 해결책은 브라우저가 페인트할 수 있도록 해당 혼잡을 해소하는 것입니다.
- RUM으로 검증: 최적화를 시작하기 전에 실제 사용자 데이터를 사용하여 Element Render Delay가 주요 LCP 병목 현상인지 확인하십시오.
- 사용하지 않는 CSS 제거: 적용되지 않는 CSS 규칙을 감사하고 제거하십시오. 이것은 가장 영향력이 큰 단일 CSS 최적화입니다. PurgeCSS 또는 DevTools의 Coverage 탭과 같은 도구를 사용하십시오.
- 스타일시트를 작고 캐시 가능하게 유지: CSS 파일당 압축 기준 약 10-15kB를 목표로 하십시오. 빠르게 다운로드할 수 있을 만큼 작으면서도 과도한 병렬 요청을 피할 수 있을 만큼 커야 합니다. 재방문 사용자를 위해 브라우저가 이를 캐시하도록 하십시오.
- 긴 JavaScript 작업을 쪼개기: 단일 스크립트는 50ms 이상 실행되어서는 안 됩니다. 렌더링 업데이트를 허용하려면 main thread에 양보(yield)하십시오.
- 서드파티 스크립트 감사 및 지연: 자문해 보십시오: 각 서드파티 스크립트가 페이지에 있어야 할 이유가 있습니까? 초기 페인트에 필수적이지 않은 것은 모두 지연시키십시오.
- SSR 또는 SSG 사용: LCP 요소를 렌더링하기 위해 클라이언트 측 JavaScript에 의존하지 마십시오. 서버에서 완전히 형성된 HTML을 보내십시오.
- 즉각적인 LCP 가시성 확보: 페이지 로드 시 LCP 요소를 숨기는 모든 애니메이션, 스크립트 또는 스타일을 제거하십시오.
content-visibility: auto사용: 긴 페이지의 경우, 화면 밖 콘텐츠의 렌더링을 건너뛰도록 브라우저에 지시하여 스크롤 위 페인팅을 위해 main thread를 확보하십시오.- DOM 크기 축소: Layout 및 Paint 작업 비용을 줄이기 위해 깊게 중첩된 HTML을 평탄화하고, 불필요한 래퍼를 제거하며, 긴 목록을 가상화하십시오.
다음 단계: 계속해서 LCP 최적화하기
Element Render Delay는 마지막 단계입니다. 4단계를 모두 다루려면 다음을 계속 진행하십시오:
- LCP 문제 수정 및 식별: 필드 데이터와 랩(lab) 도구를 사용하여 모든 LCP 문제를 찾고 해결하기 위한 완전한 진단 방법론.
- LCP 이미지 최적화: 이미지 포맷 선택, 반응형 이미지, 사전 로드 및 일반적인 이미지 최적화 실수.
- Resource Load Delay: 브라우저가 LCP 리소스를 가능한 한 일찍 발견하도록 보장하십시오. 이것은 종종 가장 큰 단일 LCP 병목 현상입니다.
- Resource Load Duration: 압축, 최신 포맷, CDN 구성 및 네트워크 최적화를 통해 다운로드 시간을 단축하십시오.

