본문 바로가기
React

[React] 초기로딩속도 줄이기! 최적화 방법

by jyee 2025. 1. 19.
728x90
반응형

로딩 속도가 3초가 넘어가면 50%이상의 사용자가 떠나며 5초를 넘으면 90%의 사용자가 떠난다는 통계가 있다. 

그만큼 초기 렌더링 속도 개선은 중요하다.

그런데 지금 회사 내 React 초기 렌더링 속도가 굉장히 느려서 속도 개선이 필요하다고 생각했고 최적화에 대해서도 알아보고 싶었기에 하나도 모르는 상태에서 호기롭게... 도전을 하게되었다. 

하나씩 검색하면서  찾아보게 된 여러 최적화 방법들을 정리한 기록들이다. 

 


Lighthouse 이용하기

여러 블로그들을 돌려보니 lighthouse랑 pageSpeed Insights가 많이 나왔다.

Lighthouse는 웹페이지 품질을 개선하는데 도움이 되는 오픈소스 자동화 도구이며 성능, 접근성, 검색엔젠 최적화 등 

 

Lighthouse 툴을 이용해서 웹사이트를 분석해봤는데 대략적으로 웹사이트의 품질을 검사할 수 있다. 

결과는 Lighthouse 탭에서 바로 확인할 수 있고, json 등으로 저장해서 Lighthouse Report Viewer 사이트에서 이전 결과들을 다시 확인 할 수 있다.

https://googlechrome.github.io/lighthouse/viewer/

 

Lighthouse Report Viewer

 

googlechrome.github.io

 

검사를 진행하면 제일 먼저 대표적으로 4가지가 나눠져서 점수가 매겨진다.

  1.  Performance   -  웹 페이지의 로딩 속도 등 실제 성능을 측정한다.
  2.  Accessibilit     -  웹 페이지의 접근성을 측정한다.
  3.  Best Practices -  웹 페이지가 웹에 대한 표준 모범 사례를 따르고 있는지 확인한다.
  4.  SEO (Search Engine Optimization) - 검색 엔진 수집 최적화가 된 정도를 측정한다.

돌려보니 역시나 Performance부분이 난리났다.

 

다음은 Lighthouse의 여섯가지 metric에 대한 설명과 가중치 값이다.

First Contentful Paint 최초 콘텐츠가 포함된 페인트. 초기 DOM 콘텐츠를 렌더링하는데 걸리는 시간을 측정 10%
Speed Index 속도 지수. 콘텐츠가 시각적으로 표시되는 진행 속도를 측정 (ex. 리로드되는 페이지의 비디오를 캡쳐하여 프레임 간의 속도를 계산) 10%
Largest Contentful Paint 가장 큰 콘텐츠가 포함된 페인트. 가장 큰 콘텐츠를 렌더링 하는데 걸리는 시간 25%
Time to interactive 상호 작용까지의 시간. 사용자가 페이지와 완전하게 상호작용할 수 있을 때까지 걸리는 시간 10%
Total Blocking Time 총 차단 시간. 마우스 클릭, 화면 탭 또는 키보드 누름과 같은 사용자 입력에 페이지가 응답하지 못하도록 차단된 총 시간 30%
Cumulative Layout Shift 누적 레이아웃 이동. 사용자가 예상치 못한 레이아웃 이동을 경험하는 것에 대한 점수 15%

 

 

문제점 ( 파면 팔수록 문제점 밖에 안 보임) 

이미지도 문제고 react-icons도 문제고

그외 지피티를 통해 얻은 문제 원인들... 

  • 605 Requests
    • 요청 수가 지나치게 많습니다. 일반적으로 초기 로딩 시 요청 수는 50~150개 이하로 유지하는 것이 이상적입니다.
    • 원인: 이미지, 스크립트, 스타일 파일, API 요청 등이 과도하게 분리되었거나, 불필요한 리소스 요청이 포함되었을 가능성.
  • 30.2 MB Transferred
    • 30MB는 초기 로딩 시 지나치게 큰 데이터 양입니다.
    • 원인:
      • 고화질 이미지를 압축하지 않음.
      • 불필요하거나 최적화되지 않은 JavaScript 번들.
      • 리소스 캐싱이 제대로 설정되지 않음.
  • DOMContentLoaded: 1.71s / Load: 1.97s
    • 이 값은 현재 네트워크 환경이 빠른 경우에도 로딩 속도가 완전히 최적화되지 않았음을 나타냅니다.
    • 브라우저가 DOM과 리소스를 처리하는데 병목이 있을 가능성이 큽니다.

 


해결방법 1. 이미지 압축하기 

찾아보니 이미지 압축도 브라우저에서 진행하는 방법이 있고 서버빌드에서 압축하는 방법이 있었다. 

 

브라우저에서 이미지압축: 유저가 파일에 이미지를 첨부할 때 대용량 이미지를 올릴 수 있는 경우가 있는데, 

이 경우 업로그 가능한 이미지의 용량을 제한해버리는 방법도 있지만 일반적으로 대부분의 유저들이 이미지 크기를 생각하며 파일을 업로드 하지 않으므로, 유저의 이미지 제한경고창을 띄우는 건 UX관점에 좋지 않다. 

그래서 공통적으로 많이 나왔던  browser image compression 라이브러리를 통해 클라이언트 단에서 이미지의 압축을 진행하여 서버에 넘겨주는 것으로 브라우저에서 압축을 진행하는 방법이 있다. 

하지만 이렇게 알아만 두고 브라우저에서 압축은 진행하지 않았다. 

 

내가 한 경우에는 서버빌드에서 이미지 압축하였는데  vite.config.ts 파일이 기존에 생성되어 있었기에 

우선 vite를 써서 빌드 때 쓰고 있었는데 여기서 몇가지 플러그인을 활용해서 압축하였다. 

+++) 나중에 알게 된 사실이였지만 둘 중 하나만 써도 되는거였다 🥲 그래서 나는 dev에서도 압축된 걸 볼 수 있는 imagetools로 하였다.

 

주요 차이점 비교

  vite-plugin-imagemin vite-imagetools
목적 이미지 최적화 (압축) 이미지 변환 + 최적화
이미지 변환 지원하지 않음 지원 (리사이징, 포맷 변경 등)
사용 환경 빌드 시 최적화된 이미지를 생성 개발 & 빌드 시 최적화된 이미지를 동적으로 제공
개발 중 적용 여부 적용되지 않음 개발 중에도 최적화된 이미지를 확인 가능
쿼리 파라미터 지원 없음 있음 (동적 이미지 변환 가능)
설정 난이도 쉬움 조금 더 복잡함

 

 ● viteImagemin

  • 목적: 이미지 파일 크기를 줄이기 위한 이미지 압축 플러그인
  • 동작 방식:
    • 빌드 과정에서 이미지 파일을 압축하여 최적화된 파일을 생성
    • imagemin 라이브러리의 다양한 플러그인을 활용해 JPEG, PNG, GIF 등 여러 포맷의 이미지를 압축가능
    • 압축 옵션(예: imageminPngQuant)을 설정하여 품질이나 압축 방식을 제어할 수 있음
  • 주 사용 시점:
    • 이미지 파일을 정적 파일로 사용하며, 빌드 시 이미지를 최적화하려는 경우.
    • 이미지 파일 크기를 줄여 네트워크 로드 속도를 개선하고자 할 때 

1. vite-plugin-imagemin 설치

npm install vite-plugin-imagemin --save-dev

 

 

2. vite.config.ts에 추가하기

import { defineConfig } from 'vite';
import viteImagemin from 'vite-plugin-imagemin';

export default defineConfig({
  plugins: [
    viteImagemin({
      plugins: {
        png: imageminPngQuant({ 
          quality: [0.4, 0.6],
        }),
      },
      root: path.resolve(__dirname),
      verbose: true,
    }),
  ],
});

3. 프로젝트 빌드

이제 프로젝트를 빌드하면 (npm run build), 이미지 파일들이 자동으로 압축됨 

 

여기까지 했을때는 이미지 압축이 된 거 같은데 이상하게 dev모드에서 보면 이미지 압축이 전혀 사용이 안되는것이다. 그래서 뭐가 문제이지 해서 다시 찾아보고 dev에서도 압축된 파일을 쓸 수 있도록 다른 플러그인을 추가하였다. 

 

 

● viteImagetools 

1. vite-Imagetools  설치

npm i vite-imagetools

2. vite.config.ts에 추가하기

    imagetools({
      defaultDirectives: new URLSearchParams({
        format: "webp",
        quality: "80",
        w: "800",
        fit: "cover",
      }),
    }),

3. 프로젝트 빌드

npm run build하고 서버에서 압축 되어있는지 확인 및 이미지의 type이 webp로 사용되어있는지 확인해보기 

용도

  • 이미지 변환과 최적화를 모두 지원하는 플러그인
  • 다양한 변환 작업(리사이징, 포맷 변경, 크롭 등)을 수행하고, 최적화된 이미지를 제공
  • 개발 중에도 이미지가 최적화된 상태로 보여지므로 즉시 확인 가능.

주요 특징

  • 개발 및 빌드 환경에서 이미지 변환 및 최적화를 제공
  • 이미지 URL에 쿼리 파라미터를 추가해 동적으로 이미지 크기나 형식을 조정 가능  예시 : ?w=800&format=webp로 이미지 폭을 800px로 조정하고 WebP 포맷으로 변환.
  • 자동으로 브라우저에서 지원하는 최적의 이미지 포맷(WebP, AVIF 등)을 제공.
  • 사용 목적: 이미지 변환 + 최적화를 통해 동적이고 유연한 이미지 처리가 가능.

 

이전에 비해 이미지 줄어든거 같은데.... 왜.... 여전히 경고일까.... 의문 여튼 많이 줄긴했음

 imagetools만 써도 될거 같아서 개발자모드에서도 이 이미지들이 압축된 걸 확인할 수 있도록 하였다. 

 

 


해결방법 2.  Code Splitting 

Code Splitting (코드 스플리팅)

처음에 여러 검색을 하면서 찾아봤을때 정말 너무 혼란스러웠는데 그 이유는 가장 많이 나온게 lazy와 suspense이였기 때문에 나는 이 방법만이 code splitting 인줄 알았기 때문이다. 하지만 이거 역시 여러 방법이 있다는 사실을 나중에서야 알게 되었고... 아래에 쓴 코드 스플리팅 방법과 비교를 해보니  lazy와 Suspense는 컴포넌트 단위의 코드 스플리팅에 초점을 두는 것이다. react의 lazy는 동적 Import()를 통해 필요한 컴포넌트를 청크 단위로 분리한다. suspense는 컴포넌트가 로드될때까지 로딩상태를 표시하거나 대체 콘텐츠를 렌더링 할 수 있게 해준다.

 

code splitting이란 웹팩을 통해 번들링된 파일이 하나일 경우 페이지에 사용하지 않는 모듈들까지 들어가 파일이 커질 수 있기 때문에 번들링된 파일이 여러개 될 수 있도록 나누는 것을 의미한다. 

webpack,rollup,browserify와 같은 모듈 번들러를 이용하여 만들어진 하나의 번들 파일을 여러개의 번들 파일로 나누는 것을 의미한다. 하나의 번들 파일을 여러개의 파일로 나누는 이유는 더 빠른 속도로 화면을 로드하기 위한 것이다. 

 

 

 

● Webpack Bundle Analyzer 

https://www.npmjs.com/package/webpack-bundle-analyzer

https://www.npmjs.com/package/rollup-plugin-visualizer 

용량별로 시각화하여 어떠한 라이브러리가 많이 사용되는지 나타내서 알 수 있다.

 

근데 내가 까막눈이라 봐도 모르겠음 그래서 막막..

멘토님께 sos 쳐서 분할됨! 코드스플리트를 제대로 안해서였다.

이 과정이 내가 헷갈렸던 부분인 코드스플리팅의 여러방법에 대해 알게 된 시점이였다😖

 

 

 vite.config.ts에 아래 코드 추가하기

build: {
    rollupOptions: {
      output: {
        manualChunks: (id: string | undefined) => {
          if(!id) {
            return undefined; 
          }
          if (id.includes("node_modules")) {
            const module = id.split("node_modules/").pop()?.split("/")[0];
            return vendor/${module};
          }
        },
      },
    },
  },

 

 


해결방법 3. 아이콘 라이브러리 사이즈 줄이기 

당시 react-icon을 쓰고 있었는데 문제는 이 icon의 번들사이즈가 너무나도 컸다. 

트리세이킹으로 해결해야겠다 해서 코드를 써봤는데 뭐 잘못 썼는지 에러가 났다. 그래서 왜 안되는거지 싶어서 찾아보니 react-icon 자체가 트리세이킹으로 줄일 수가 없는? 그런 문제가 있어서 

고민하다가 그냥 다른 icon 라이브러리를 쓰는게 더 빠를 수도 있겠다 싶어서

멘토님께서 추천해준 두 가지 antdmaterial 중에 정하기로 하였다.

찾아보니 둘 다 많이 쓰이는거 같은데 그래도 material이 구글에서 나온거니깐 더 안정적이지 않을까 싶어서

material icon으로 넘어왔고 하나씩 수정해가면서 바꿔주었다.

왼쪽: 전 오른쪽: 후

정말 용량 많이 줄어들었다 근데 퍼포먼스 점수는 여전하다 

++ 알고보니 lucid-react 라는 아이콘이 또 쓰이고 있었다🤦‍♀️

그래서 material로 다 통일 시키고 이 lucid-react를 없앴다.

 

 

드라마틱한 점수개선이 없어서 아쉬웠지만 그래도 하나씩 찾아가보면서 점수를 끌어올리는 자체가 흥미로웠다.

팀 내 물어볼 사람이 없어서 혼자서 삽질도 많이 하긴 했었지만 이렇게 조금이나마 최적화에 대해 찾아보고 알아가니 나중에 더 많이 공부해보고 싶다는 생각도 들었다. 

728x90
반응형