LCP 요소 렌더링 지연을 최적화하세요.

다운로드부터 화면 표시까지: Largest Contentful Paint의 요소 렌더링 지연을 개선하는 방법을 알아보세요.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-07-10

이 가이드는 Core Web Vitals 리소스 센터의 Largest Contentful Paint (LCP) 섹션에 속한다. 요소 렌더링 지연(Element Render Delay)은 LCP 타임라인의 마지막 단계다. LCP 리소스 다운로드가 끝나고 화면에 시각적으로 표시될 때까지의 간격을 나타낸다.

LCP 요소 렌더링 지연 최적화

LCP 4단계 중 요소 렌더링 지연은 가장 오해가 많은 단계다. 개발팀은 TTFB를 최적화하고, 리소스 로드 지연을 제거하며, 자산을 압축해 리소스 로드 시간을 단축한다. 네트워크 폭포수(waterfall)가 끝나는 것을 보고 작업이 완료되었다고 가정한다. 이는 틀렸다.

요소 렌더링 지연은 LCP 리소스 다운로드가 끝난 시점부터 사용자 화면에 요소가 완전히 그려질 때까지의 시간이다. 이는 네트워크 문제가 아니다. main thread 문제다. 렌더링 지연이 길다는 것은 브라우저가 이미지나 폰트를 가지고 있지만 다른 작업으로 너무 바빠서 실제로 그리지 못하고 있음을 의미한다. 이 지연은 LCP 점수에 직접적인 타격을 준다. 모든 네트워크 요청이 완료된 후에도 때로는 200ms 이상의 지연을 추가한다.

정확한 정의: 라스트 마일 문제

요소 렌더링 지연은 LCP 리소스(예: 이미지 파일 또는 웹 폰트)의 마지막 바이트가 브라우저에 도착하는 순간 시작된다. LCP 요소가 화면에 시각적으로 그려지면 끝난다. 말 그대로 정말 마지막 단계다.

시스템 폰트를 사용하는 텍스트 기반 LCP 요소의 경우 외부 리소스가 필요 없으므로 이 지연 시간은 종종 0이다. 하지만 LCP 요소가 이미지이거나 커스텀 웹 폰트를 사용하는 대다수 사이트에서는 이 단계가 가장 큰 병목이 된다. 브라우저는 다운로드한 비트를 눈에 보이는 픽셀로 변환하는 CPU 바운드 작업에 이 시간을 소비한다.

이유: 꽉 막힌 조립 라인

렌더링 지연을 해결하려면 브라우저가 페이지를 그리는 방식을 이해해야 한다. 이는 흔히 중요 렌더링 경로(Critical Rendering Path)라고 불리는 다단계 프로세스다. 이를 공장 조립 라인이라고 생각하자.

  1. 설계도 구성 (DOM 및 CSSOM): 브라우저는 HTML을 파싱하여 DOM(Document Object Model)을 만들고, CSS를 파싱하여 CSSOM(CSS Object Model)을 만든다. 이는 페이지 콘텐츠와 스타일에 대한 설계도다.
  2. 설계도 결합 (Render Tree): DOM과 CSSOM을 결합하여 렌더 트리를 만든다. 여기에는 페이지를 렌더링하는 데 필요한 노드만 포함된다. <head> 요소나 display: none;이 적용된 요소는 제외된다.
  3. 도형 계산 (Layout): 브라우저는 렌더 트리에 있는 모든 요소의 정확한 크기와 위치를 계산한다. 이 단계는 "리플로우(reflow)"라고도 한다.
  4. 픽셀 칠하기 (Paint): 브라우저는 텍스트, 색상, 이미지, 테두리, 그림자 등을 고려하여 각 요소의 픽셀을 채운다.
  5. 레이어 조립 (Composite): 페이지를 여러 레이어에 그린 다음, 올바른 순서로 조립하여 최종 화면 이미지를 만든다.

요소 렌더링 지연은 Layout, Paint, Composite 등 최종 단계에서 소비되는 시간이다. 이 전체 조립 라인은 단일 작업자인 main thread에 의해 실행된다. 이 작업자가 JavaScript long task를 실행하거나 방대한 CSS 파일을 파싱하느라 바쁘면 조립 라인이 멈춘다. LCP 이미지가 도착했더라도, main thread가 자유로워져 처리하고 그릴 때까지 하역장에서 대기해야 한다.

요소 렌더링 지연을 정확히 찾아내는 방법

이 문제를 진단하려면 엄격한 2단계 프로세스를 따른다. 첫 번째 단계를 건너뛰지 마라.

1단계: field data (RUM)로 검증
DevTools를 열기 전에, 요소 렌더링 지연이 실제 사용자에게 진짜 문제인지 확인해야 한다. 내가 만든 CoreDash와 같은 전문가 수준의 RUM 도구가 필수적이다. 사이트의 LCP를 4개의 하위 단계로 분류해 준다. RUM 데이터의 75백분위수에서 유의미한 요소 렌더링 지연이 나타난다면, 해결해야 할 영향력 높은 문제가 검증된 것이다.

2단계: DevTools로 진단
RUM에서 문제 페이지를 파악한 후에는 Chrome DevTools Performance 패널을 사용하여 원인을 찾는다. Chrome DevTools Performance 패널로 LCP 진단하기 가이드에서 스로틀링 설정과 기록 워크플로우를 단계별로 다룬다. 렌더링 지연을 구체적으로 확인하려면 다음을 따른다:

  1. "Record and reload" 버튼으로 페이지 로드를 기록한다.
  2. Insights 사이드바에서 "LCP breakdown" insight를 열고 Element render delay 값을 확인한다.
  3. 이제 타임라인에서 Main 트랙을 검사한다. LCP 리소스 네트워크 요청이 끝난 시점과 LCP 타이밍 마커 사이에서 발생하는 long task(빨간 모서리가 있는 노란색 블록)를 찾는다. 이러한 task가 지연의 직접적인 원인이다. 마우스를 올려 원인이 되는 스크립트를 식별한다.

일반적인 원인과 영향력 높은 해결책

요소 렌더링 지연이 긴 것은 거의 항상 차단된 main thread 때문이다.

원인: render blocking CSS

문제: 기본적으로 CSS는 render blocking이다. 브라우저는 <head>에 연결된 모든 CSS 파일을 다운로드하고 파싱할 때까지 픽셀을 그리지 않는다. 크고 복잡한 스타일시트는 수백 밀리초 동안 main thread를 점유하여 layout 및 paint 단계의 시작을 지연시킬 수 있다. 사이트가 여러 스타일시트를 로드할 때 문제가 심각해지며, 각각 별도의 네트워크 요청과 파싱 주기가 필요하다. CSS payload를 줄이는 전략에 대한 자세한 내용은 사용하지 않는 CSS 제거하기 가이드를 참조하라.

해결책: CSS를 작고, 깔끔하며, 캐시 가능하게 만들어라.

  • 사용하지 않는 CSS 제거: 가장 효과가 큰 최적화다. 대규모 사이트에서 사용하지 않는 CSS는 전체 스타일시트 크기의 70% 이상을 차지할 수 있다. PurgeCSS와 같은 도구는 HTML과 JavaScript를 스캔하여 사용하지 않는 선택자를 식별한다. 사용하지 않는 규칙을 제거하면 다운로드 시간과 main thread의 파싱 시간이 모두 줄어든다.
  • 작고 캐시 가능한 스타일시트 목표: CSS 파일의 이상적인 크기는 대략 10-15kB(압축 기준)다. 이보다 작으면 너무 많은 병렬 요청으로 분할될 위험이 있으며, 각각 연결 오버헤드가 발생한다. 이보다 크면 특히 느린 모바일 네트워크에서 차단 시간이 늘어난다. 이 범위 내의 잘 구조화된 단일 스타일시트는 빠르게 다운로드되고 빠르게 파싱되며, 재방문 시 브라우저에 의해 캐시된다.
  • 인라인 CSS는 최후의 수단으로만 사용: 중요 CSS를 <style> 블록에 인라인(inline)하면 첫 페이지 로드의 네트워크 요청은 사라지지만 대가가 따른다. 인라인 CSS는 브라우저 캐시가 불가능하다. 모든 재방문자는 매 페이지마다 이를 다시 다운로드해야 한다. 재방문 사용자가 있는 대부분의 사이트에서는 브라우저가 캐시할 수 있는 작은 외부 스타일시트가 더 나은 선택이다. 인라인은 재방문자가 거의 없는 랜딩 페이지에만 의미가 있다.

CSS 영향 정량화: CSS가 렌더링 지연에 얼마나 기여하는지 측정하려면 Chrome DevTools Coverage 탭을 열어라(Ctrl+Shift+P 누른 후 "Coverage" 입력). 페이지를 로드하고 CSS 파일에서 사용되지 않은 바이트의 비율을 확인한다. 사용하지 않는 CSS의 비율이 높다면 정리가 요소 렌더링 지연을 줄여준다는 명확한 신호다.

원인: JavaScript long task

문제: 이것이 가장 일반적인 원인이다. 프레임워크, 분석 스크립트, A/B 테스트 도구 또는 최적화되지 않은 코드로 인한 무거운 JavaScript 실행은 main thread를 독점할 수 있다. 단일 장기 실행 task는 수백 밀리초 동안 렌더링을 차단하여 요소 렌더링 지연에 직접 추가된다. Google은 long task를 50ms 이상 걸리는 작업으로 정의하며, 200ms를 초과하는 task는 치명적으로 긴 것으로 간주한다. JavaScript 지연 전략의 전체 모음은 JavaScript를 지연시키는 14가지 방법 문서를 참조하라.

해결책: 작업을 잘게 나누어라.

  • main thread로 yield: long task는 더 작은 덩어리로 쪼개야 한다. setTimeout(..., 0) 또는 최신 scheduler.yield() API를 사용하여 브라우저로 정기적으로 제어권을 yielding함으로써 이를 수행할 수 있다. 이를 통해 브라우저는 작업 사이에 렌더링 업데이트를 수행할 수 있다.
  • 서드파티 최적화 및 지연: 모든 서드파티 스크립트를 감사하라. 초기 렌더링에 필수적이지 않다면 defer 속성으로 로드하거나 페이지 로드 후에 주입하라. A/B 테스트용 스크립트는 의도적으로 렌더링을 차단하는 경우가 많아 특히 문제가 된다.
  • 시각적 업데이트에 requestAnimationFrame 사용: JavaScript가 페이지 로드 중 DOM 조작을 수행해야 하는 경우 해당 작업을 requestAnimationFrame으로 감싸라. 이렇게 하면 다음 paint 직전에 작업이 실행되도록 예약되어, 브라우저가 JavaScript 작업 사이에 프레임을 그릴 수 있는 기회를 보장한다.

DevTools에서 long task 식별하기

Chrome DevTools Performance 패널에서 long task는 "Main" 트랙의 우측 상단에 빨간색 삼각형이 있는 노란색 블록으로 표시된다. 원인이 되는 스크립트를 식별하려면 다음을 수행하라.

  1. Performance 패널에서 페이지 로드를 기록한다.
  2. Timings 트랙에서 LCP 마커를 찾는다.
  3. Main 트랙에서 LCP 리소스의 네트워크 요청 완료 시점과 LCP 마커 사이에서 발생하는 long task를 검사한다.
  4. 이러한 task를 클릭하여 Summary 패널에서 콜스택을 확인한다. 콜스택은 long task의 원인이 되는 소스 파일과 함수를 보여준다.

문제를 일으키는 흔한 서드파티 스크립트

실제 컨설팅 경험에 비추어 볼 때, 요소 렌더링 지연을 일으키는 가장 흔한 서드파티 스크립트는 다음과 같다.

  • A/B 테스트 도구 (Optimizely, VWO, AB Tasty): 실험군 간의 콘텐츠 깜빡임을 방지하기 위해 종종 의도적으로 렌더링을 차단한다. 실험 결정을 서버 측으로 옮기면(서버 사이드 테스트) 이 문제를 완전히 없앨 수 있다.
  • 동기식 태그가 있는 태그 관리자: 동기식(비동기가 아닌) 태그로 구성된 태그 관리자는 render blocking 스크립트를 주입할 수 있다. 컨테이너를 감사하여 모든 태그가 DOM ready 또는 window load 이후에 실행되도록 설정되었는지 확인하라.
  • 동의 관리 플랫폼: 결정이 내려질 때까지 렌더링을 차단하는 쿠키 동의 배너는 LCP를 지연시킬 수 있다. 중요 렌더링 경로를 차단하지 않는 비동기 구현을 사용하라.
  • 채팅 위젯: 라이브 채팅 스크립트는 페이지 로드 시 무거운 초기화 코드를 실행하는 경우가 많다. 페이지가 상호작용 가능해질 때까지 로드를 지연시키거나 사용자 상호작용(예: 클릭) 시 로드하라.

원인: 클라이언트 사이드 렌더링(CSR)

문제: 순수 클라이언트 사이드 렌더링의 경우 초기 HTML에 LCP 요소가 없는 경우가 많다. 먼저 JavaScript가 실행되어 DOM을 빌드하고 LCP 요소를 삽입해야 브라우저가 마침내 렌더링할 수 있다. 이 전체 과정이 하나의 거대한 렌더링 지연이다.

해결책: 서버에서 렌더링하라. 다른 방법은 없다. SSR(Server-Side Rendering) 또는 SSG(Static Site Generation)를 사용하여 서버에서 전송한 초기 HTML 문서에 LCP 요소가 존재하도록 하라. 이렇게 하면 JavaScript 주도의 전체 렌더링 단계가 지연 원인에서 배제된다.

원인: 다른 코드에 의해 숨겨진 콘텐츠

문제: LCP 요소가 DOM에 있지만 CSS(예: opacity: 0) 또는 스크립트에 의해 숨겨진 경우가 있다. 스크롤 시 드러나는 애니메이션이나 어떤 변형을 보여줄지 결정 중인 A/B 테스트 도구가 그 예다. 요소가 다운로드되어 준비되었지만 아직 보이지 않아 그릴(paint) 수 없다.

해결책: 즉시 보이도록 보장하라. LCP 요소의 경우, 초기 로드 시 이를 숨기는 입장 애니메이션이나 어떠한 로직도 사용하지 마라. 요소는 DOM에 표시되어야 하고 첫 번째 paint부터 보이도록 스타일을 지정해야 한다. A/B 테스트 도구가 비동기적으로 실행되도록 구성하거나 LCP 요소의 가시성에 미치는 영향을 최소화하라.

원인: 과도한 DOM 크기

문제: 큰 DOM(1,500개 이상의 노드)은 모든 렌더링 작업 비용을 증가시킨다. 각각의 레이아웃 계산, 스타일 재계산, paint 작업에서 더 많은 노드를 처리해야 하므로 main thread에서 더 많은 시간이 걸린다. CSS와 JavaScript가 잘 최적화되어 있더라도, 비대해진 DOM은 엄청난 양만으로도 렌더링 지연을 추가한다. DOM 크기를 줄이는 자세한 전략은 과도한 DOM 크기 방지하기 가이드를 참조하라.

해결책: 초기 렌더링에 참여하는 DOM 노드 수를 줄여라.

  • HTML 구조 단순화: 불필요한 래퍼(wrapper) 요소를 제거하라. 깊게 중첩된 구조를 평탄화하라. 레이아웃에 추가적인 <div> 요소 대신 CSS Grid 또는 Flexbox를 사용하라.
  • 긴 목록 가상화: 수백 개의 목록 항목(제품 그리드, 데이터 테이블)이 있는 페이지의 경우 viewport에 현재 표시되는 항목만 렌더링하는 가상화 라이브러리를 사용하라.
  • 스크롤 아래 콘텐츠 지연 렌더링: 화면 밖 섹션의 렌더링을 완전히 건너뛰려면 content-visibility: auto(아래에서 다룸)를 사용하라.

고급 전술: 렌더링 완벽 제어

복잡한 애플리케이션은 main thread에 대한 더 많은 제어가 필요하다.

content-visibility로 성능 높이기

CSS content-visibility 속성은 큰 페이지를 위해 만들어졌다. 페이지의 스크롤 아래(below the fold) 섹션에 content-visibility: auto;를 설정하면, 브라우저에 해당 콘텐츠가 viewport에 들어오기 직전까지 레이아웃, paint 및 composite 작업을 건너뛸 수 있음을 알려준다. 이렇게 하면 초기 렌더링 작업량이 줄어들고 main thread가 자유로워져 LCP 요소를 더 빨리 그릴(paint) 수 있다.

핵심은 content-visibility: auto와 함께 숨겨진 콘텐츠에 임시 크기를 제공하는 contain-intrinsic-size를 짝지어 사용하는 것이다. 이것이 없으면 브라우저가 숨겨진 섹션의 높이를 알 수 없어 스크롤바 동작이 불규칙해진다.

/* 스크롤 아래 섹션에 적용 */
.below-fold-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px; /* 섹션의 예상 높이 */
}

/* 예시: 긴 게시물 페이지 */
.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배 줄였다. 브라우저는 이러한 섹션에 대해 레이아웃, paint 및 composite 작업을 완전히 건너뛰고, main thread가 LCP 요소를 포함한 상단(above-fold) 콘텐츠에 집중하도록 해방시킨다. 브라우저 지원 범위는 Chromium, Firefox, Safari 18+ 등 모든 최신 브라우저를 포함한다.

웹 워커(Web Workers)로 작업 분산

웹 워커를 사용하면 main thread를 완전히 벗어나 백그라운드 스레드에서 JavaScript를 실행할 수 있다. 워커에서 실행되는 무거운 연산은 렌더링을 차단하지 않는다. 이 사이트(corewebvitals.io)는 분석 처리에 웹 워커를 사용하며 성능상 이점은 확실하다. main thread가 방해 없이 화면을 그릴(paint) 수 있는 자유로운 상태를 유지한다.

그렇긴 하지만 웹 워커는 대부분의 웹사이트에서 흔한 패턴이 아니다. 별도의 JavaScript 파일과 postMessage를 통한 통신이 필요하며 DOM에 접근할 수 없다. 대부분의 CMS 플랫폼과 사이트 빌더는 기본 지원을 제공하지 않아 커스텀 개발 없이 구현하기 어렵다. 기술력이 있다면 main thread를 비워두는 가장 효과적인 방법 중 하나다. 하지만 대부분의 개발팀에게는 이 페이지의 다른 최적화 방법들이 더 큰 실질적인 영향을 줄 것이다.

// main.js: 워커를 생성하고 처리를 위해 데이터 전송
const worker = new Worker('/js/analytics-worker.js');

// 무거운 분석 처리를 워커 스레드로 분산
worker.postMessage({
  type: 'process-events',
  events: collectedEvents
});

// main thread를 차단하지 않고 결과 수신
worker.onmessage = (event) => {
  console.log('Analytics processed:', event.data.summary);
};

// analytics-worker.js: 백그라운드 스레드에서 실행
self.onmessage = (event) => {
  if (event.data.type === 'process-events') {
    // main thread 밖에서 무거운 연산 실행
    const summary = processEvents(event.data.events);
    self.postMessage({ summary });
  }
};

실제 사례

  • 사례 1: render blocking CSS 병목: DebugBear는 대용량 CSS 파일이 뚜렷한 렌더링 지연을 일으키는 사이트를 분석했다. LCP 이미지는 다운로드되었지만 브라우저는 CSS를 파싱하느라 멈춰 있었다. 단순히 중요 CSS를 인라인으로 처리함으로써 브라우저는 HTML 파싱 직후 LCP 요소를 포함한 페이지 콘텐츠를 그릴 수 있었고, 스타일시트로 인한 렌더링 지연이 효과적으로 제거되었다.
  • 사례 2: A/B 테스트의 부작용: 한 대형 이커머스 사이트는 동기식 A/B 테스트 스크립트 때문에 LCP가 지연되고 있음을 발견했다. LCP 이미지는 빠르게 다운로드되었지만 어떤 제품 이미지를 표시할지 결정하는 동안 스크립트가 main thread를 차단했다. 중요하지 않은 요소에 대해 초기 페이지 로드 이후에 A/B 테스트가 실행되도록 옮기자 즉시 LCP가 400ms 이상 개선되었다. 이 모든 시간은 요소 렌더링 지연에서 회복된 것이다.

체크리스트: 요소 렌더링 지연 제거 방법

요소 렌더링 지연이 길다는 것은 main thread가 혼잡하다는 뜻이다. 해결책은 브라우저가 paint를 수행할 수 있도록 이 혼잡을 해소하는 것이다.

  1. RUM으로 검증: 최적화를 시작하기 전에 실제 사용자 데이터를 사용하여 요소 렌더링 지연이 LCP의 주요 병목인지 확인하라.
  2. 사용하지 않는 CSS 제거: 절대 적용되지 않는 CSS 규칙을 감사하고 제거하라. 이는 가장 효과가 큰 CSS 최적화다. PurgeCSS 또는 DevTools의 Coverage 탭과 같은 도구를 사용하라.
  3. 작고 캐시 가능한 스타일시트 유지: CSS 파일당 약 10-15kB(압축 기준)를 목표로 하라. 빠르게 다운로드될 만큼 작고, 과도한 병렬 요청을 피할 만큼 커야 한다. 재방문자를 위해 브라우저가 캐시하도록 하라.
  4. JavaScript long task 쪼개기: 어떤 단일 스크립트도 50ms 이상 실행되어서는 안 된다. 렌더링 업데이트를 허용하도록 main thread로 yield 하라.
  5. 서드파티 스크립트 감사 및 지연: 각 서드파티 스크립트가 페이지에 있어야 할 이유가 있는지 자문해 보라. 초기 paint에 필수적이지 않은 모든 것을 지연시켜라.
  6. SSR 또는 SSG 사용: 클라이언트 사이드 JavaScript에 의존하여 LCP 요소를 렌더링하지 마라. 서버에서 완전히 형성된 HTML을 전송하라.
  7. LCP 즉시 가시성 확보: 페이지 로드 시 LCP 요소를 숨기는 모든 애니메이션, 스크립트 또는 스타일을 제거하라.
  8. content-visibility: auto 사용: 긴 페이지의 경우 브라우저가 화면 밖 콘텐츠 렌더링을 건너뛰도록 지시하여 main thread가 상단(above-fold) paint에 집중할 수 있게 하라.
  9. DOM 크기 줄이기: 깊게 중첩된 HTML을 평탄화하고, 불필요한 래퍼를 제거하며, 긴 목록을 가상화하여 레이아웃과 paint 작업 비용을 줄여라.

다음 단계: LCP 최적화 계속하기

요소 렌더링 지연은 마지막 단계다. 네 가지 단계를 모두 다루려면 다음을 계속 읽어보라.

  • LCP 문제 해결 및 식별: field data와 랩 도구를 사용하여 모든 LCP 문제를 찾고 수정하는 완전한 진단 방법론.
  • LCP 이미지 최적화: 이미지 포맷 선택, 반응형 이미지, 프리로딩, 흔한 이미지 최적화 실수들.
  • 리소스 로드 지연: 브라우저가 LCP 리소스를 최대한 일찍 발견하도록 보장하라. 이는 종종 가장 큰 단일 LCP 병목 현상이다.
  • 리소스 로드 시간: 압축, 최신 포맷, CDN 구성 및 네트워크 최적화를 통해 다운로드 시간을 단축하라.

관리 안 하는 순간 퍼포먼스는 무너집니다.

모니터링, 퍼포먼스 버짓, 프로세스까지 세팅합니다. 일회성 수정과 진짜 해결의 차이가 바로 거기서 갈립니다.

한번 얘기해봐요
LCP 요소 렌더링 지연을 최적화하세요. Core Web Vitals LCP 요소 렌더링 지연을 최적화하세요.