“페이지는 잘 뜨는데, 왜 이렇게 느리게 느껴지지?”
화면은 잘 그려져도 실제 사용자가 빠르다고 느끼려면 리소스(이미지, JS, 폰트 등)의 최적화가 필수이다.
이번 글에서는 브라우저에서 자주 사용되는 리소스를 어떻게 가볍고 효율적으로 관리할 수 있는지 실전 위주로 정리한다.
이미지는 대부분의 웹페이지에서 용량의 절반 이상을 차지한다. 가벼운 이미지 포맷을 사용하는 것만으로도 속도가 확연히 달라진다.
| 포맷 | 특징 |
|---|---|
| WebP | PNG/JPEG보다 25~35% 더 가벼움, 대부분 브라우저 지원 |
| AVIF | 더 높은 압축률, 아직 일부 브라우저에서만 지원 |
<picture> 태그로 다중 포맷 대응<picture>
<source srcset="img.avif" type="image/avif" />
<source srcset="img.webp" type="image/webp" />
<img src="img.jpg" alt="예시 이미지" />
</picture>
지원되는 포맷 중 가장 좋은 걸 브라우저가 자동으로 선택함
srcset, sizes사용자의 화면 크기에 따라 이미지 용량을 다르게 제공하면 불필요한 데이터 낭비를 막을 수 있다.
<img
src="img-600.jpg"
srcset="img-600.jpg 600w, img-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="반응형 이미지"
/>
번들 파일이 너무 커지면 초기 로딩이 느려진다. 그래서 코드를 나눠서 필요한 순간에만 로드하는 게 코드 스플리팅이다.
import { lazy, Suspense } from 'react';
const AdminPage = lazy(() => import('./AdminPage'));
<Suspense fallback={<div>Loading...</div>}>
<AdminPage />
</Suspense>
초기에는 로드되지 않다가, 해당 라우트에 접근할 때만 불러옴
Lazy Loading은 요소(이미지, 컴포넌트, 모듈)를 필요한 시점에 로드하는 전략이다.
<img src="banner.jpg" loading="lazy" alt="..." />
const SomeModule = await import('./heavy-module.js');
웹폰트는 사용자에게 FCP 지연을 유발하는 대표적인 리소스다.
| 전략 | 설명 |
|---|---|
font-display: swap | 기본 폰트 먼저 보여주고 웹폰트 로딩되면 교체 |
| 서브셋 폰트 | 한글, 숫자만 따로 서브셋 생성 (경량화) |
| preload 사용 | 렌더링 전 폰트 미리 불러오기 |
<link rel="preload" as="font" href="/fonts/myfont.woff2" type="font/woff2" crossorigin="anonymous" />
preload, prefetch, preconnect 전략preload렌더링에 즉시 필요한 리소스를 미리 불러옴 (예: 폰트, 메인 이미지, JS 모듈 등)
<link rel="preload" href="/main.js" as="script" />
prefetch나중에 필요할 리소스를 브라우저가 여유 있을 때 미리 받아둠
<link rel="prefetch" href="/next-page.js" />
preconnect외부 리소스(예: Google Fonts)의 DNS → TLS 연결을 미리 맺어놓기
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
<picture>로 포맷 대응<img loading="lazy">는 바로 적용 가능하고 효과도 큼웹 성능은 리소스를 어떻게 다루느냐에 따라 결정된다. 이미지, JS, 폰트 등 페이지의 무게를 줄이고, 필요한 순간에만 불러오는 구조로 설계하면 실제 체감 성능이 크게 개선된다.