IT·개발· 10분 읽기
🚀

Core Web Vitals 개선 실전 — LCP 3초→1.2초 줄인 방법

LCP(Largest Contentful Paint)는 Google 검색 순위에 직접 영향을 주는 핵심 지표다. 이미지 최적화, 폰트 로딩 전략, 코드 스플리팅 세 가지를 순차 적용해 LCP를 3.0초에서 1.2초로 단축했다.

광고

핵심 요약

>

- LCP(Largest Contentful Paint)는 Google 검색 순위에 직접 영향을 주는 핵심 지표다. - 이미지 최적화, 폰트 로딩 전략, 코드 스플리팅 세 가지를 순차 적용해 LCP를 3.0초에서 1.2초로 단축했다. - CLS는 이미지·광고 영역에 명시적 크기를 지정하고 레이아웃 변화를 제거해 0.28→0.04로 낮췄다. - 실제 프로덕션 Next.js 프로젝트 기준이며, 동일 원리를 React·Vue 프로젝트에도 적용할 수 있다.


들어가며 — 왜 Core Web Vitals인가

2021년 Google이 Page Experience 업데이트를 배포하면서 Core Web Vitals는 단순한 UX 지표를 넘어 SEO 순위 요소로 자리 잡았다. 그 이후로도 Google은 꾸준히 해당 신호의 가중치를 높이고 있으며, 2024년 기준 INP(Interaction to Next Paint)가 FID를 완전히 대체하면서 평가 항목도 진화했다.

필자가 운영하는 이커머스 프로젝트는 반년 전까지만 해도 PageSpeed Insights에서 모바일 점수 38점이라는 처참한 성적을 받고 있었다. LCP는 3.0초, CLS는 0.28, INP는 320ms — 세 지표 모두 "개선 필요" 구간이었다. 그 상태에서 어떻게 지표를 바꿨는지, 과정 전체를 기록으로 남긴다.


Core Web Vitals 세 가지를 빠르게 이해하는 방법은?

LCP — 페이지의 "첫인상" 속도

LCP는 뷰포트 안에서 가장 큰 콘텐츠 요소(대개 히어로 이미지나 H1 텍스트)가 렌더링되는 데 걸리는 시간이다. Google 기준 2.5초 이하가 Good, 4.0초를 초과하면 Poor다.

이커머스 사이트에서 히어로 배너 이미지가 LCP 대상이 되는 경우가 압도적으로 많다. 이 이미지 하나를 제때 불러오지 못하면 나머지 최적화가 모두 무의미해진다.

CLS — 레이아웃이 "펄쩍" 뛰지 않는가

CLS(Cumulative Layout Shift)는 페이지 로딩 중 요소가 예기치 않게 이동하는 정도를 측정한다. 광고 배너가 뒤늦게 삽입되거나, 크기를 지정하지 않은 이미지가 뒤늦게 로드되면 점수가 높아진다. 0.1 이하가 Good 기준이다.

INP — 사용자 입력에 얼마나 빠르게 반응하는가

INP는 클릭, 탭, 키보드 입력 등 모든 상호작용의 응답 지연을 측정한다. 2024년 3월 FID를 대체했으며, 200ms 이하가 Good 기준이다. JavaScript 번들이 무거울수록 메인 스레드가 막혀 INP가 나빠진다.


문제 진단 — 처음 측정 결과

최적화 전 지표를 정확히 기록하는 것이 출발점이다. 아래 도구를 순서대로 사용했다.

  1. PageSpeed Insights — 필드 데이터(실제 사용자 데이터)와 랩 데이터(Lighthouse) 동시 제공
  2. Chrome DevTools > Performance 탭 — 렌더링 타임라인과 LCP 후보 요소 확인
  3. WebPageTest — 국내 CDN 엣지 서버 기준 실측, 필름스트립으로 시각 확인
  4. Vercel Analytics / Sentry — 실제 유저 세션 기반 Core Web Vitals 수집

진단 결과 병목은 세 곳으로 좁혀졌다.

  • 히어로 이미지 — 4.2MB JPEG, 최적화 없이 태그로 직접 삽입
  • Google Fonts@import 방식으로 3개 폰트 패밀리를 동기 로드
  • 번들 크기 — 메인 청크 1.8MB, 트리 셰이킹 미적용 라이브러리 다수 포함

LCP를 3.0초에서 1.2초로 줄인 세 가지 방법은?

방법 1 — 이미지 최적화와 preload

가장 효과가 컸다. 히어로 이미지 처리 방식을 완전히 바꿨다.

Before

<img src="/banner.jpg" alt="메인 배너" />

After — Next.js Image 컴포넌트 + WebP 변환

import Image from 'next/image';

<Image
  src="/banner.webp"
  alt="메인 배너"
  width={1920}
  height={800}
  priority          // LCP 대상 이미지는 반드시 priority
  quality={80}
  sizes="(max-width: 768px) 100vw, 1920px"
/>

priority prop 하나만 붙여도 Next.js는 해당 이미지에 태그를 자동으로 삽입한다. WebP 전환 이후 파일 크기는 4.2MB에서 340KB로 약 92% 감소했다.

순수 HTML/React 프로젝트라면 안에 직접 preload 태그를 추가하면 된다.

<link
  rel="preload"
  as="image"
  href="/banner.webp"
  type="image/webp"
/>

이 작업 하나로 LCP가 3.0초 → 1.8초로 떨어졌다.

방법 2 — 폰트 로딩 전략 변경

Google Fonts를 @import로 불러오면 브라우저는 CSS를 파싱하다가 멈추고 폰트 요청을 보낸다. 이 대기 시간이 LCP를 크게 늘린다.

Before

<style>
  @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap');
</style>

After — + display=swap + 자체 호스팅

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap"
/>

더 나아가 next/font(Next.js 13+)를 사용하면 폰트를 빌드 타임에 자체 호스팅으로 처리해 외부 요청 자체가 사라진다.

// app/layout.js
import { Noto_Sans_KR } from 'next/font/google';

const notoSansKR = Noto_Sans_KR({
  weight: ['400', '700'],
  subsets: ['latin'],
  display: 'swap',
});

폰트 개선 후 LCP 1.8초 → 1.5초.

방법 3 — 코드 스플리팅과 번들 최적화

메인 JS 번들이 크면 브라우저의 메인 스레드가 파싱·실행으로 묶인다. 이 시간 동안 LCP 요소가 화면에 그려지지 않는다.

다이나믹 임포트로 초기 번들 분리

import dynamic from 'next/dynamic';

// 스크롤 아래 위치하는 무거운 차트 컴포넌트
const SalesChart = dynamic(() => import('@/components/SalesChart'), {
  ssr: false,
  loading: () => <div className="chart-skeleton" />,
});

번들 분석으로 불필요한 라이브러리 제거

npx @next/bundle-analyzer

분석 결과 moment.js(66KB gzip)와 lodash 전체(70KB gzip)가 포함된 것을 확인했다. momentdate-fns의 필요한 함수만 개별 임포트로 교체하고, lodashlodash-es의 트리 셰이킹 임포트로 전환했다.

// Before
import _ from 'lodash';
const result = _.groupBy(data, 'category');

// After
import { groupBy } from 'lodash-es';
const result = groupBy(data, 'category');

메인 번들이 1.8MB → 420KB로 줄었고, LCP는 1.5초 → 1.2초로 내려갔다.


CLS 0.28 → 0.04 낮추기

레이아웃 이동의 주범은 두 곳이었다.

1. 크기 미지정 이미지

모든 태그에 widthheight를 명시하거나, CSS로 aspect-ratio를 고정했다.

.product-image-wrapper {
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

2. 뒤늦게 삽입되는 광고 배너

광고 슬롯이 로드되기 전에 동일한 높이의 플레이스홀더를 미리 잡아두었다.

<div style={{ minHeight: '90px' }}>
  <AdBanner slot="header" />
</div>

이 두 가지만으로 CLS가 0.28에서 0.04로 감소했다.


INP 개선 — 메인 스레드 부담 줄이기

INP는 번들 경량화 후 자연스럽게 320ms → 140ms로 개선되었다. 추가로 클릭 이벤트 핸들러 안에서 무거운 연산을 동기로 실행하던 부분을 requestIdleCallback으로 미뤘다.

button.addEventListener('click', () => {
  // 즉시 필요한 UI 업데이트
  setLoading(true);

  // 무거운 처리는 유휴 시간으로 연기
  requestIdleCallback(() => {
    processHeavyData(payload);
  });
});

최적화 전후 비교

지표최적화 전최적화 후기준
LCP3.0초1.2초Good: ≤ 2.5초
CLS0.280.04Good: ≤ 0.1
INP320ms140msGood: ≤ 200ms
PageSpeed (모바일)38점82점
메인 번들1.8MB420KB

FAQ

Q1. Core Web Vitals 점수가 SEO 순위에 얼마나 영향을 미치나요?

Google은 Core Web Vitals를 "tie-breaker" 신호로 설명한다. 콘텐츠 품질이 비슷한 두 페이지 중 사용자 경험이 더 좋은 쪽을 우선한다는 의미다. 절대적인 가중치는 공개되지 않았지만, 특히 경쟁이 치열한 키워드에서 차별 요소가 될 수 있다. 모바일 성능 개선 후 해당 프로젝트에서 타겟 키워드 평균 순위가 약 5~8계단 상승했다.

Q2. PageSpeed Insights와 실제 사용자 경험이 다른 이유는 무엇인가요?

PageSpeed Insights는 두 가지 데이터를 제공한다. 상단의 "Field Data"는 Chrome UX Report 기반의 실제 유저 데이터이고, 하단의 "Lab Data"는 Lighthouse가 제어된 환경에서 시뮬레이션한 결과다. 실제 성능은 네트워크 조건, 기기 사양, 캐시 상태에 따라 달라지므로 Lab 점수만 보지 말고 Field Data를 반드시 함께 확인해야 한다.

Q3. LCP 대상 요소가 무엇인지 어떻게 확인하나요?

Chrome DevTools의 Performance 탭에서 녹화 후 타임라인을 보면 LCP 마커와 해당 요소가 표시된다. 또는 PageSpeed Insights 결과 페이지에서 "LCP element" 항목을 직접 확인할 수 있다. 대부분은 히어로 이미지, 대형 텍스트 블록, 배경 이미지 중 하나다.

Q4. Next.js를 사용하지 않아도 동일한 최적화를 적용할 수 있나요?

가능하다. 핵심 원리는 프레임워크와 무관하다. LCP 이미지 preload는 순수 HTML 태그로 구현할 수 있고, 코드 스플리팅은 Webpack의 import()나 Vite의 다이나믹 임포트로 동일하게 처리된다. 폰트 자체 호스팅은 google-webfonts-helper 같은 도구로 파일을 받아 직접 서빙하면 된다.

Q5. 이미지를 WebP로 변환하면 구형 브라우저에서 문제가 생기지 않나요?

2024년 기준 WebP 지원율은 전 세계 브라우저의 97% 이상이다. IE 11이 주요 고객층이 아닌 이상 실질적으로 문제가 없다. 혹시 구형 지원이 필요하다면 태그로 JPEG 폴백을 제공하면 된다.

<picture>
  <source srcset="/banner.webp" type="image/webp" />
  <img src="/banner.jpg" alt="메인 배너" />
</picture>

Q6. CLS가 왜 모바일에서 더 심하게 나타나나요?

모바일 기기는 CPU와 네트워크 성능이 데스크톱보다 낮아 리소스 로딩 순서가 달라질 가능성이 높다. 또한 화면 크기가 작아 작은 레이아웃 이동도 상대적으로 큰 시프트 값으로 집계된다. Google의 Lighthouse 시뮬레이션도 기본적으로 Moto G4 수준의 모바일 기기를 기준으로 한다. 모바일 PageSpeed 점수를 먼저 개선하는 것이 효율적인 이유다.


마치며

Core Web Vitals 최적화는 한 번에 완성되지 않는다. 코드가 바뀌고 서드파티 스크립트가 추가될 때마다 지표는 다시 흔들린다. Vercel Analytics나 Sentry 같은 도구로 실사용자 데이터를 지속적으로 모니터링하고, CI/CD 파이프라인에 Lighthouse CI를 붙여 PR마다 점수를 체크하는 습관을 들이는 것이 장기적으로 가장 효과적이다.

시작이 막막하다면 PageSpeed Insights 결과 페이지의 "Opportunities" 항목부터 하나씩 따라가면 된다. 대부분의 문제는 이미지 크기, 렌더 블로킹 리소스, 미사용 JavaScript 세 가지 안에 있다.

광고

🔧 이 글과 관련된 무료 도구

이 글과 관련된 상품 (Core Web Vitals)[광고/제휴]

이 포스팅은 쿠팡 파트너스, 아마존 어소시에이트, 알리익스프레스 제휴 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다. 이는 상품 가격에 영향을 주지 않습니다.
As an Amazon Associate, Coupang Partner, and AliExpress affiliate, I earn from qualifying purchases at no extra cost to you.

관련 글

광고