웹 성능 최적화 체크리스트 2026
Core Web Vitals, 이미지 최적화, 번들 사이즈, 서버 응답 속도까지. 2026년 기준 웹 성능 체크리스트.
사이트가 느리면 사용자가 떠난다. 구글은 이걸 수치로 증명했다. 페이지 로딩이 1초에서 3초로 늘어나면 이탈률이 32% 증가한다. 5초면 90%. 성능은 기능이 아니라 기본값이어야 한다.
그런데 "성능 최적화 해야지"라고 마음먹어도 뭐부터 건드려야 할지 모르겠는 경우가 많다. 이 글은 2026년 기준으로 웹 성능을 점검할 때 확인해야 할 항목을 정리한 체크리스트다.
Core Web Vitals
구글이 밀고 있는 성능 지표. 검색 순위에 영향을 주기 때문에 SEO 관점에서도 신경 써야 한다. 2024년에 FID가 INP로 교체되면서 측정 기준이 바뀌었다.
LCP (Largest Contentful Paint)
페이지에서 가장 큰 콘텐츠 요소가 화면에 렌더링되기까지의 시간. 보통 메인 이미지나 큰 텍스트 블록이 해당된다.
- 좋음: 2.5초 이하
- 개선 필요: 2.5~4초
- 나쁨: 4초 초과
LCP가 느린 원인은 대부분 네 가지다:
- 서버 응답이 느림. TTFB(Time to First Byte)가 길면 LCP도 늦어진다.
- 렌더링 차단 리소스. CSS, JS 파일이 로딩되기 전까지 렌더링이 멈춘다.
- 리소스 로딩이 느림. 메인 이미지가 크거나 CDN이 없거나.
- 클라이언트 사이드 렌더링. JS가 다 로드되고 실행된 뒤에야 콘텐츠가 보이는 구조.
LCP 요소가 이미지라면 fetchpriority="high"를 설정하고, preload 힌트를 넣는 게 즉각적인 개선 방법이다.
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
INP (Interaction to Next Paint)
사용자 인터랙션(클릭, 탭, 키 입력) 후 다음 프레임이 렌더링될 때까지의 시간. 기존 FID가 "첫 번째 인터랙션만" 측정했다면, INP는 페이지 전체 수명 동안의 모든 인터랙션을 측정한다.
- 좋음: 200ms 이하
- 개선 필요: 200~500ms
- 나쁨: 500ms 초과
INP가 나쁜 주범은 메인 스레드를 오래 점유하는 JavaScript다. 긴 태스크(50ms 이상)가 있으면 사용자 입력에 대한 반응이 밀린다.
개선 방법:
- 긴 태스크를
requestIdleCallback이나scheduler.yield()로 분할 - 무거운 계산은 Web Worker로 이동
- 불필요한 리렌더링 방지 (React의 경우
memo,useMemo,useCallback) - 이벤트 핸들러에서 DOM 변경 최소화
CLS (Cumulative Layout Shift)
레이아웃이 예상치 못하게 밀리는 현상의 누적값. 읽고 있던 텍스트가 광고 로딩 때문에 갑자기 아래로 밀리는 그런 경험.
- 좋음: 0.1 이하
- 개선 필요: 0.1~0.25
- 나쁨: 0.25 초과
CLS를 잡으려면:
- 이미지에 반드시
width,height속성 명시 (또는aspect-ratioCSS) - 웹 폰트 로딩 시
font-display: swap과 함께 fallback 폰트 크기 맞추기 (size-adjust) - 광고 영역에 최소 높이 예약
- 동적 콘텐츠 삽입 시 공간 미리 확보
이미지 최적화
이미지가 페이지 용량의 절반 이상을 차지하는 경우가 많다. 여기를 건드리는 게 가장 효과가 크다.
포맷 선택
2026년 기준 권장 포맷:
- AVIF — 가장 효율적. WebP 대비 20~30% 더 작은 용량에 동등한 품질. 브라우저 지원도 이제 충분하다.
- WebP — AVIF 다음으로 효율적. 레거시 브라우저 대응용 fallback으로 적합.
- SVG — 아이콘, 로고, 일러스트 같은 벡터 이미지는 무조건 SVG.
- PNG/JPEG — 최후의 fallback.
<picture> 태그로 포맷별 fallback을 구현한다:
<picture>
<source srcset="/image.avif" type="image/avif">
<source srcset="/image.webp" type="image/webp">
<img src="/image.jpg" alt="설명" width="800" height="600">
</picture>
Next.js를 쓴다면 next/image 컴포넌트가 이걸 자동으로 처리한다.
지연 로딩
뷰포트에 보이지 않는 이미지를 미리 로딩할 필요가 없다.
<img src="/below-fold.webp" loading="lazy" alt="설명">
단, LCP 요소(히어로 이미지 등)에는 loading="lazy"를 쓰면 안 된다. 오히려 느려진다. LCP 이미지는 loading="eager"가 기본이고, fetchpriority="high"까지 추가하는 게 좋다.
반응형 이미지
데스크톱용 2000px 이미지를 모바일에서 그대로 로딩하면 낭비다.
<img
srcset="/img-400.webp 400w, /img-800.webp 800w, /img-1200.webp 1200w"
sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1200px"
src="/img-800.webp"
alt="설명"
>
번들 사이즈 줄이기
JavaScript 번들이 크면 파싱과 실행에 시간이 걸린다. 특히 모바일 기기에서 체감이 심하다.
현재 상태 파악
먼저 번들 분석부터. 뭐가 큰지 모르면 줄일 수도 없다.
# Next.js
npx @next/bundle-analyzer
# Vite
npx vite-bundle-visualizer
# 범용
npx source-map-explorer dist/assets/*.js
대부분의 경우 놀랍게도 npm 패키지 몇 개가 번들의 절반 이상을 차지하고 있다. moment.js(locale 파일), lodash(전체 임포트), 날짜 포맷팅용 대형 라이브러리 같은 것들.
트리 쉐이킹
사용하지 않는 코드를 번들에서 제거한다. ESM(ES Modules) 기반이면 빌드 도구가 자동으로 처리하는데, CommonJS 모듈은 트리 쉐이킹이 안 된다.
// 나쁜 예 — lodash 전체를 가져옴
import _ from 'lodash';
_.debounce(fn, 300);
// 좋은 예 — 필요한 함수만 가져옴
import debounce from 'lodash/debounce';
debounce(fn, 300);
동적 임포트
모든 코드를 초기에 로딩할 필요 없다. 사용자가 특정 기능을 쓸 때 로딩하면 된다.
// 모달을 열 때만 차트 라이브러리를 로딩
const openChart = async () => {
const { Chart } = await import('chart.js');
// ...
};
Next.js에서는 next/dynamic으로 컴포넌트 단위 코드 스플리팅이 가능하다.
서버 응답 최적화
클라이언트에서 아무리 최적화해도 서버가 느리면 한계가 있다.
TTFB 줄이기
서버가 첫 바이트를 응답하기까지의 시간. 800ms 이하가 권장이고, 200ms 이하면 우수.
- CDN 사용. 사용자와 가까운 엣지 서버에서 응답. Cloudflare, AWS CloudFront, Vercel Edge 등.
- 서버 사이드 캐싱. 매 요청마다 DB 쿼리를 날리지 않도록. Redis, 인메모리 캐시 활용.
- 데이터베이스 최적화. 쿼리 튜닝, 인덱스 설정, N+1 문제 해결.
캐싱 전략
Cache-Control: public, max-age=31536000, immutable
정적 에셋(JS, CSS, 이미지)은 장기 캐싱 + 파일명 해시로 버전 관리. HTML은 짧은 캐싱(max-age=0, must-revalidate)이나 stale-while-revalidate 패턴.
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300
CDN에서 60초간 캐싱하되, 만료 후에도 5분간은 캐시된 버전을 먼저 보여주고 백그라운드에서 갱신한다.
압축
Gzip은 기본이고, Brotli를 쓰면 Gzip 대비 15~20% 추가 압축이 된다. 대부분의 모던 브라우저가 Brotli를 지원한다. 서버(Nginx, Cloudflare 등)에서 설정하면 된다.
측정 도구
최적화 전후를 측정하지 않으면 개선인지 아닌지 알 수 없다.
- Lighthouse — Chrome DevTools에 내장. 종합 점수와 개선 제안을 준다.
- PageSpeed Insights — 실제 사용자 데이터(CrUX)를 기반으로 성능을 보여준다. 실험실 데이터와 필드 데이터를 같이 볼 수 있다.
- WebPageTest — 다양한 네트워크 조건, 디바이스, 지역에서 테스트 가능. 워터폴 차트가 유용하다.
- Chrome DevTools Performance 탭 — 런타임 성능 프로파일링. 메인 스레드에서 뭐가 오래 걸리는지 추적.
Lighthouse 점수에 집착하기보다는 실제 사용자 경험 데이터를 보는 게 낫다. PageSpeed Insights의 필드 데이터 섹션이 그 역할을 한다.
체크리스트 요약
빠르게 훑어볼 수 있게 정리하면:
- LCP 2.5초 이하
- INP 200ms 이하
- CLS 0.1 이하
- 이미지 AVIF/WebP 변환
- LCP 이미지에 preload + fetchpriority="high"
- 뷰포트 밖 이미지에 loading="lazy"
- 반응형 이미지 (srcset, sizes)
- JavaScript 번들 분석 후 불필요한 의존성 제거
- 동적 임포트로 코드 스플리팅
- CDN 적용
- 정적 에셋 장기 캐싱
- Brotli 압축 활성화
- 웹 폰트 최적화 (subset, font-display)
- 서드파티 스크립트 지연 로딩
전부 한번에 할 필요는 없다. Lighthouse로 현재 상태를 측정하고, 점수가 가장 낮은 항목부터 하나씩 개선하면 된다. 이미지 최적화 하나만 제대로 해도 체감이 확 달라지는 경우가 많다.