Core Web Vitals 실전 디버깅 — Chrome DevTools로 CLS, LCP 원인 찾기
PageSpeed Insights에서 "Performance 74점"을 보면 "어디부터 고쳐야 하지?"라는 생각이 듭니다. 점수 아래에 나열된 항목들이 20개가 넘으니까요. 점수를 내리는 진짜 원인을 정확히 찾아야 효율적으로 고칠 수 있습니다.
Chrome DevTools와 몇 가지 도구를 사용해서, CLS는 어떤 요소가 움직이는지, LCP는 어떤 요소가 늦게 그려지는지 정확히 추적하는 방법을 정리했습니다.
1CLS 원인 요소 찾기
PageSpeed 결과에서 CLS가 높다고 나오면, 어떤 요소가 얼마나 움직였는지 알아야 합니다. 세 가지 방법이 있습니다.
방법 1: PageSpeed 감사 항목
PageSpeed 결과 페이지 하단에 "레이아웃 변경 방지" 항목이 있습니다. 여기서 어떤 요소가 CLS를 발생시키는지 확인할 수 있습니다.
// PageSpeed JSON 응답에서 CLS 요소 찾기
// .lighthouseResult.audits["layout-shift-elements"]
{
"id": "layout-shift-elements",
"details": {
"items": [
{
"node": {
"selector": "div.typewriter-container > span",
"snippet": "<span class=\"text-4xl font-bold\">"
},
"score": 0.341 // 이 요소의 CLS 기여도
},
{
"node": {
"selector": "header > nav > div.auth-section",
"snippet": "<div class=\"flex items-center\">"
},
"score": 0.089
}
]
}
}selector와 snippet을 보면 어떤 요소인지 바로 특정할 수 있습니다. score가 높을수록 해당 요소의 CLS 기여도가 큽니다.
방법 2: Performance 탭 Layout Shift
Chrome DevTools Performance 탭에서 더 자세한 분석이 가능합니다:
Layout Shift 하이라이트
Performance 녹화 중 Experience 행을 보면 Layout Shift 이벤트가 표시됩니다. 각 이벤트를 클릭하면 어떤 노드가 어디서 어디로 이동했는지 좌표까지 나옵니다. CLS 0.5가 여러 번의 작은 shift인지, 한 번의 큰 shift인지도 구분됩니다.
방법 3: Console에서 직접 관찰
// Console에 붙여넣기 — CLS 실시간 모니터
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.hadRecentInput) continue; // 사용자 입력은 무시
console.log("Layout Shift:", entry.value.toFixed(4));
for (const source of entry.sources || []) {
console.log(" Element:", source.node?.nodeName,
source.node?.className);
console.log(" Moved:", source.previousRect, "→",
source.currentRect);
}
}
}).observe({ type: "layout-shift", buffered: true });2LCP 요소 식별하기
LCP를 개선하려면 먼저 LCP 요소가 뭔지 알아야 합니다. 생각보다 의외의 요소가 LCP일 수 있습니다.
// PageSpeed JSON에서 LCP 요소 확인
// .lighthouseResult.audits["largest-contentful-paint-element"]
{
"id": "largest-contentful-paint-element",
"details": {
"items": [{
"node": {
"selector": "div.hero > img",
"snippet": "<img src=\"/images/hero.avif\" />"
}
}]
}
}
// Console에서도 확인 가능
new PerformanceObserver((list) => {
const entries = list.getEntries();
const last = entries[entries.length - 1];
console.log("LCP Element:", last.element);
console.log("LCP Time:", last.startTime, "ms");
console.log("LCP Size:", last.size, "px²");
}).observe({ type: "largest-contentful-paint", buffered: true });LCP가 이미지가 아닐 수 있다
LCP는 "가장 큰 콘텐츠 요소"입니다. Hero 이미지가 작거나 뒤늦게 나타나면, 큰 텍스트 블록이 LCP가 될 수 있습니다. 특히 motion/react로 이미지에 opacity:0을 넣으면, Lighthouse가 이미지를 건너뛰고 텍스트를 LCP로 잡습니다.
3Performance 탭 활용
Performance 탭의 타임라인에서 Core Web Vitals 이벤트를 한눈에 볼 수 있습니다.
| 마커 | 색상 | 의미 | 확인 방법 |
|---|---|---|---|
| FCP | 초록 | 첫 콘텐츠 렌더링 | Timings 행의 초록 마커 |
| LCP | 초록 | 최대 콘텐츠 렌더링 | Timings 행의 LCP 마커 |
| Layout Shift | 분홍 | 레이아웃 변동 | Experience 행의 분홍 블록 |
| Long Task | 빨강 줄무늬 | 50ms 이상 메인 스레드 차단 | Main 행의 빨간 줄무늬 블록 |
Performance 탭 팁
- Network throttling 설정: Performance 탭에서도 네트워크 쓰로틀링을 설정할 수 있습니다. "Fast 3G"로 설정하면 PageSpeed의 Mobile 환경과 비슷해집니다.
- CPU throttling 설정: 톱니바퀴 → CPU: 4x slowdown으로 설정하면 모바일 환경을 시뮬레이션합니다.
- Screenshots 활성화: 녹화 시 "Screenshots" 체크하면 시점별 화면을 볼 수 있습니다. CLS가 발생하는 순간을 시각적으로 확인 가능합니다.
4curl로 SSR HTML 분석
브라우저에서 보이는 HTML은 JavaScript가 실행된 후의 결과입니다. 서버가 보내는 원본 HTML을 확인하려면 curl을 사용합니다. motion/react의 opacity:0 문제는 브라우저에서 절대 찾을 수 없습니다 — JS가 즉시 바꿔버리니까요.
# SSR HTML에서 opacity:0 찾기
curl -s https://example.com | grep -i "opacity"
# 결과 예시:
# <div style="opacity:0;transform:translateY(60px)">
# → motion/react가 인라인으로 넣은 초기 상태!
# img 태그의 속성 점검
curl -s https://example.com | grep -oP '<img[^>]+>'
# 결과 예시:
# <img src="/hero.avif" width="900" height="1118"
# fetchpriority="high">
# <img src="/service.webp"> ← loading="lazy" 누락!
# preload 확인
curl -s https://example.com | grep "rel=\"preload\""
# 결과 예시:
# <link rel="preload" href="/hero.avif" as="image">
# <link rel="preload" href="/service.webp" as="image">
# ↑ 아래 fold 이미지까지 preload되고 있음!curl로 반드시 확인해야 할 것들
- opacity:0 인라인 스타일: motion/react가 SSR에서 넣은 초기 상태. LCP를 지연시킵니다.
- img 태그의 loading 속성: 아래 fold 이미지에 lazy가 있는지 확인.
- 불필요한 preload: Hero 외 이미지에 preload가 있으면 대역폭 낭비.
- font-display 값: 웹폰트 CSS에서 swap인지 optional인지 확인.
5Web Vitals 확장 프로그램
Chrome Web Store에서 "Web Vitals"를 검색하면 Google이 만든 공식 확장 프로그램이 있습니다. 설치하면 브라우저 상단에 현재 페이지의 Core Web Vitals가 실시간으로 표시됩니다.
실시간 CLS 모니터링
스크롤하거나 클릭할 때마다 CLS가 누적됩니다. 어떤 인터랙션에서 CLS가 발생하는지 실시간으로 확인 가능합니다.
LCP 요소 하이라이트
확장 프로그램을 클릭하면 현재 LCP 요소가 하이라이트됩니다. 예상과 다른 요소가 LCP일 수 있습니다.
TTFB, FCP, FID 동시 확인
Core Web Vitals 3가지(LCP, CLS, INP)와 FCP, TTFB까지 한눈에 볼 수 있습니다.
Console 로그 옵션
옵션에서 'Console Logging'을 활성화하면 DevTools Console에 상세 정보가 출력됩니다.
6디버깅 체크리스트
PageSpeed 점수가 낮을 때, 순서대로 확인하면 됩니다.
Core Web Vitals 디버깅 순서
CLS가 높을 때
LCP가 느릴 때
TBT가 높을 때
이 글의 DevTools 기능은 Chrome 120+ 기준이며, 버전에 따라 UI와 기능이 다를 수 있습니다. Web Vitals 확장 프로그램은 Chrome Web Store에서 무료로 설치할 수 있습니다.
댓글
(4개)로그인하면 댓글을 작성할 수 있습니다.
Performance 탭에서 Layout Shift 이벤트를 클릭하면 어떤 노드가 움직였는지 나온다는 거, 팀원들한테 공유했습니다. 디버깅 시간이 확 줄겠네요.
Web Vitals 확장 프로그램 깔았는데, 페이지 이동할 때마다 CLS가 실시간으로 보이는 게 신기합니다. 어떤 인터랙션에서 CLS가 발생하는지 바로 잡을 수 있어요.
curl로 SSR HTML 확인하는 방법이 제일 유용합니다. opacity:0이 인라인으로 들어가는 걸 브라우저에서는 절대 못 찾거든요. JS가 즉시 바꿔버리니까.
관련 글
© 2026 TreeRU. All rights reserved.
본 콘텐츠의 저작권은 TreeRU에 있으며, 출처를 밝히지 않은 무단 전재 및 재배포를 금합니다. 인용 시 출처(treeru.com)를 반드시 명시해 주세요.