LCP 최적화: 이미지 사전 로드, 중요한 CSS 및 SSR
Il 콘텐츠가 포함된 최대 페인트(LCP) 흐르는 시간을 측정하다 페이지를 로드하는 순간부터 가장 큰 요소가 표시되는 순간까지 뷰포트가 렌더링됩니다. Google은 2.5초 미만의 LCP를 '좋음'으로 간주합니다. 2.5초에서 4초 사이는 "개선 예정", 4초 이상은 "나쁨"입니다. 셋중 하나야 검색 순위에 직접적인 영향을 미치는 핵심 웹 바이탈입니다.
LCP를 최적화하려면 다음 사항을 이해해야 합니다. 임계 경로: 순서 브라우저가 요소를 렌더링하기 전에 완료해야 하는 작업 LCP. 중요 경로에 불필요한 리소스가 있으면 LCP가 지연됩니다. 네 가지 전략 더 큰 영향 — 이미지 사전 로딩, 중요한 인라인 CSS, 최적화 글꼴 및 서버 측 렌더링 — 대부분의 경우 LCP를 500ms-2초로 줄입니다. 실제 사례의 일부입니다.
무엇을 배울 것인가
- 브라우저가 LCP 요소를 검색하고 로드하는 방법: 주요 경로
- fetchpriority="high": 브라우저에 어떤 이미지가 우선순위인지 알려줍니다.
- rel="preload": 파서가 리소스를 발견하기 전에 리소스를 미리 로드합니다.
- 중요한 CSS 인라인: 스타일시트의 렌더링 차단 제거
- 글꼴 디스플레이: LCP를 지연시키는 FOIT를 피하기 위해 교체 및 선택 사항
- 서버 측 렌더링: LCP용 워터폴 JS 제거
- WebPageTest 및 PerformanceObserver로 실제 영향을 측정하세요.
LCP의 중요 경로
최적화하기 전에 특정 사례에서 LCP를 차단하는 것이 무엇인지 이해해야 합니다. 브라우저는 다음 단계에 따라 LCP 요소를 렌더링합니다.
// PerformanceObserver: misura il tuo LCP reale
const observer = new PerformanceObserver((list) => {
// L'ultimo entry e il piu aggiornato
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP Element:', lastEntry.element?.tagName);
console.log('LCP Time:', lastEntry.startTime.toFixed(0), 'ms');
console.log('LCP URL:', lastEntry.url); // se e un'immagine
// Breakdown del tempo LCP
if (lastEntry.element) {
const el = lastEntry.element;
const rect = el.getBoundingClientRect();
console.log('Element size:', rect.width * rect.height, 'px^2');
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
// Quali elementi vengono considerati per l'LCP?
// - img elements
// - image elements dentro svg
// - video elements (usa il poster)
// - background-image (solo via CSS)
// - block-level elements con testo (h1, p, div con testo visibile)
// L'elemento piu grande nel viewport al momento del paint
// Come trovare l'elemento LCP in DevTools:
// 1. Apri Chrome DevTools > Performance
// 2. Registra un caricamento
// 3. Cerca "LCP" nella timeline
// 4. Clicca sull'entry: mostra quale elemento
fetchpriority="high": 명시적인 브라우저 우선순위
브라우저는 내부 우선순위 시스템을 사용하여 로드할 리소스를 결정합니다.
전에. 기본적으로 이미지의 우선순위는 낮음 또는 중간입니다. 속성
fetchpriority="high" 이 이미지가 LCP임을 브라우저에 알려줍니다.
요소이며 먼저 로드되어야 합니다.
중요한 CSS 인라인: 렌더링 차단 제거
외부 스타일시트는 다운로드 및 구문 분석될 때까지 렌더링을 차단합니다. 느린 네트워크 또는 느린 서버는 LCP를 500ms-2초까지만 지연할 수 있습니다. CSS 파일의 경우. 해결책: 중요한 CSS를 인라인하세요 직접 HTML(스크롤 없이 볼 수 있는 부분의 콘텐츠를 렌더링하는 데 필요한 CSS) e 나머지는 비동기적으로 로드합니다.
// Tool per estrarre automaticamente il Critical CSS
// criticalcss (npm) oppure Vite Critical CSS plugin
// Configurazione Vite con vite-plugin-critical
// vite.config.ts
import { defineConfig } from 'vite';
import critical from 'vite-plugin-critical';
export default defineConfig({
plugins: [
critical({
criticalUrl: 'http://localhost:4173',
criticalBase: 'dist/',
criticalPages: [
{ uri: '/', template: 'index' },
{ uri: '/blog', template: 'blog' },
],
criticalConfig: {
inline: true,
dimensions: [
{ width: 375, height: 812 }, // iPhone
{ width: 1440, height: 900 }, // Desktop
],
},
}),
],
});
글꼴 최적화: 중요 경로에서 FOIT 제거
웹 글꼴은 종종 LCP 요소 텍스트의 렌더링을 차단합니다. 글꼴이 아닌 경우
여전히 로드되어 있으면 브라우저는 (FOIT - 보이지 않는 텍스트 플래시)를 기다립니다.
텍스트를 렌더링합니다. font-display: swap 대체를 먼저 표시
그런 다음 교체하지만 CLS를 생성합니다. font-display: optional 그리고 선택
LCP에 더 좋음: 글꼴이 아직 캐시되지 않은 경우 대체를 사용합니다.
/* Font loading ottimizzato per LCP */
/* 1. Preconnect al CDN del font (risparmia ~100ms di DNS lookup) */
/* Nel :
*/
/* 2. Preload del font critico (quello usato dall'LCP element) */
/* Nel :
*/
/* 3. font-display strategy */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-latin-400.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: optional;
/* optional: usa il font solo se gia in cache.
Al primo caricamento usa system font (zero FOIT, zero CLS).
Dal secondo caricamento usa il web font. */
unicode-range: U+0000-00FF; /* Solo Latin: riduce dimensione */
}
/* Per testi LCP critici: usa fallback con metriche simili */
/* Riduci CLS da font swap usando size-adjust */
@font-face {
font-family: 'Inter-Fallback';
src: local('Arial'); /* Font di sistema come fallback */
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
size-adjust: 107%;
/* Metriche calibrate per ridurre il layout shift */
}
.lcp-heading {
font-family: 'Inter', 'Inter-Fallback', system-ui;
}
서버 측 렌더링: JavaScript 폭포 제거
클라이언트 측 렌더링을 사용하는 SPA(단일 페이지 애플리케이션)에 문제가 있습니다. LCP의 구조: 브라우저는 HTML을 다운로드하고 JavaScript를 실행해야 합니다. 데이터를 요청한 다음 콘텐츠를 렌더링합니다. 이 "폭포"는 LCP에 1~3초를 추가합니다. 서버 측 렌더링(SSR)이 이 문제를 해결합니다. 이미 렌더링된 HTML을 클라이언트에 보냅니다.
// Impatto SSR sull'LCP: misurazioni reali
// SPA senza SSR: waterfall tipico
// 0ms - Browser riceve HTML (vuoto, solo )
// 200ms - Scarica bundle JavaScript (150KB gzipped)
// 400ms - Esegue JavaScript, monta il framework
// 600ms - Prima render: scheletro UI (loading spinner)
// 800ms - API request per i dati
// 1200ms - Risposta API
// 1300ms - Render con dati: LCP event
// LCP = ~1300ms (buono ma potrebbe essere meglio)
// Con SSR: nessun waterfall
// 0ms - Browser invia request
// 150ms - Server renderizza HTML completo con dati
// 350ms - Browser riceve HTML gia renderizzato con l'LCP element visibile
// 350ms - LCP event: l'elemento e gia nella pagina!
// LCP = ~350ms (eccellente)
// Angular Universal (SSR) - esempio di configurazione
// angular.json: abilita SSR
// "server": { "builder": "@angular-devkit/build-angular:application" }
// app.config.server.ts:
import { ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
export const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
],
};
// Next.js: LCP ottimale con SSR e Image component
// Ogni pagina e server-rendered by default in App Router
// Il componente Image gestisce automaticamente:
// - fetchpriority="high" per above-fold images
// - preload link nel head
// - srcset per responsive images
// - dimensioni esplicite per evitare CLS
영향 측정: 이전과 이후
// Misura il tuo LCP prima di ottimizzare
// e dopo per verificare il miglioramento
// web-vitals.js: libreria Google per misurare CWV
import { onLCP, onFID, onCLS } from 'web-vitals';
function sendToAnalytics(metric) {
// Invia a Google Analytics o sistema custom
gtag('event', metric.name, {
value: Math.round(metric.value),
metric_id: metric.id,
metric_value: metric.value,
metric_delta: metric.delta,
metric_rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
});
}
onLCP(sendToAnalytics);
// WebPageTest: test sintetico con waterfall dettagliato
// https://www.webpagetest.org
// Parametri raccomandati:
// - Location: EC2 Frankfurt (per utenti EU)
// - Connection: 4G (LTE) - 9 Mbps down, 9 Mbps up, 170ms RTT
// - Repeat Views: si (testa cache)
// - Video capture: si (vedi frame-by-frame quando appare l'LCP)
// Risultati da cercare nel waterfall:
// - Quante risorse bloccano il rendering? (Resources in Critical Chain)
// - Quando viene scoperta l'immagine LCP? (piu presto = meglio)
// - Quanto dura il "Render-Blocking" bar? (dovrebb essere < 200ms)
LCP 최적화 체크리스트
- DevTools Performance 또는 WebPageTest를 사용하여 LCP 요소 식별
- fetchpriority="high"를 추가하고 LCP 요소에서 loading="lazy"를 제거합니다.
- 이미지의 경우: AVIF/WebP, 반응형 srcset, 명시적 크기 사용
- 중요한 CSS를 스크롤 없이 볼 수 있는 부분에 인라인하고 나머지는 비동기로 로드합니다.
- LCP 요소에 사용되는 글꼴을 미리 로드합니다.
- SSR을 사용하는 경우: LCP 요소가 초기 HTML에 있는지 확인하세요.
- 프로덕션 환경에서 CrUX 또는 web-vitals.js를 사용하여 LCP P75 측정
결론
LCP 최적화는 기본적으로 시간을 줄이는 연습입니다. 주요 경로: 브라우저가 렌더링하기 전에 다운로드해야 하는 모든 리소스 LCP 요소는 개선의 기회입니다. 가져오기 우선순위를 사용하면 중요한 CSS 인라인 및 SSR은 일반적인 모바일 연결에서 1초 미만의 LCP를 달성할 수 있습니다.







