프로젝트를 진행하며 부트캠프, 팀프로젝트 등으로 인해 글로 정리하는 부분은 소홀했습니다.

프로젝트를 마무리 하였으며, 성능 최적화 단계를 진행한 글을 써봅니다.


  - 시작점: 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

728x90

'무비넷' 카테고리의 다른 글

로컬스토리지 연결(워치리스트 컴포넌트)  (0) 2025.05.29
[React] 클릭시 이미지 변경하기  (0) 2025.05.28
[React]리액트 타입 연결  (0) 2025.05.23
프롭스 연결하기  (0) 2025.05.22
프로젝트 시작  (0) 2025.05.08