웹앱 성능 최적화
프로젝트를 진행하며 부트캠프, 팀프로젝트 등으로 인해 글로 정리하는 부분은 소홀했습니다.
프로젝트를 마무리 하였으며, 성능 최적화 단계를 진행한 글을 써봅니다.
- 시작점: 81점 (개선 필요)
- 최종 결과: 첫 방문 95점, 재방문 98점 (우수)
- 핵심 전략: 이미지 최적화 + 지연 로딩 + 캐싱 전략
- 용량 절약: 5.3MB → 1.1KB (99.98% 절약)
기본 최적화 (81점 → 92점)
1. 이미지 최적화 (가장 큰 효과)
PNG를 WebP로 변환하면서 크기도 최적화
cwebp -resize 48 48 -q 80 greyheart.png -o greyheart.webp
cwebp -resize 24 24 -q 80 triangleDown.png -o triangleDown.webp
- greyheart: 1.4MB → 392 bytes (99.97% 절약)
- triangle icons: 1.3MB → 248 bytes (99.98% 절약)
- 총 절약량: 5.3MB → 1.1KB
2. 빌드 최적화
// vite.config.ts
export default defineConfig({
build: {
minify: 'esbuild',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
cssCodeSplit: true,
sourcemap: false,
},
})
이미지 지연 로딩 (92점 → 93점)
모든 외부 이미지에 loading="lazy" 속성 추가
// Before
https://image.tmdb.org/t/p/w185${actor.profile_path}`} alt={actor.name} />
// After
https://image.tmdb.org/t/p/w185${actor.profile_path}`}
alt={actor.name}
loading="lazy"
/>
적용 범위 : 13개 컴포넌트, 영화 포스터, 출연진 사진, OTT 로고 등
그 외 최적화 (93점 → 95점/98점)
폰트 최적화
// 폰트 비동기 로딩으로 렌더링 차단 방지
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap"
rel="stylesheet"
media="print"
onload="this.media='all'" />
API 서버 DNS 미리 해석
<link rel="dns-prefetch" href="//api.themoviedb.org" />
<link rel="dns-prefetch" href="//image.tmdb.org" />
<link rel="dns-prefetch" href="//http://www.omdbapi.com" />
Critical CSS 인라인화
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans KR', system-ui, sans-serif;
background-color: #0a0a0a;
color: #ffffff;
-webkit-font-smoothing: antialiased;
}
.loading {
display: flex; justify-content: center; align-items: center;
height: 50vh; color: #ff8500;
}
</style>
Service Worker 캐싱
// 이미지 : Stale While Revalidate (즉시 응답 + 백그라운드 업데이트)
if (url.hostname === 'image.tmdb.org') {
return cache.match(request).then((cachedResponse) => {
const fetchPromise = fetch(request).then((networkResponse) => {
cache.put(request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
});
}
// API : Network First with 캐시 폴백
if (url.hostname === 'api.themoviedb.org') {
return fetch(request)
.then((response) => {
if (response.ok) {
cache.put(request, response.clone());
}
return response;
})
.catch(() => caches.match(request));
}
결과
- 첫방뭉 : 95점
- 재방문 : 98점
- 점수 향상 : +17점 (21% 개선)
- LCP : 2.8초 -> 1초 미만 (64% 개선)
- FCP : 1.0초 -> 0.5초 미만 (50% 개선)
정리
이미지 최적화는 단일 작업으로 11점을 향상하여 가장 큰 효과를 가져왔습니다.
이외에도 단순한 기능 구현 외에 사용자 경험 향상을 위하여 성능 최적화를 처음으로 경험해보았는데, 결과물은 실제로 아주 강한 만족감으로 나타났습니다. 이는 더욱 사용자의 입장에서 개발을 할 수 있게 해주는 좋은 경험이였습니다.
https://developers.google.com/web/tools/lighthouse
https://developers.google.com/speed/webp
https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook
'무비넷' 카테고리의 다른 글
| 로컬스토리지 연결(워치리스트 컴포넌트) (0) | 2025.05.29 |
|---|---|
| [React] 클릭시 이미지 변경하기 (0) | 2025.05.28 |
| [React]리액트 타입 연결 (0) | 2025.05.23 |
| 프롭스 연결하기 (0) | 2025.05.22 |
| 프로젝트 시작 (0) | 2025.05.08 |