본문 바로가기
실무/성능 최적화

번들 최적화

by 김홍중 2025. 3. 16.

(이전글)  windowing기법과 실무 적용

https://hongjungkim-dev.tistory.com/1355

 

windowing기법

적용전모든 리스트를 DOM에 추가하게됩니다. 1만개를 넘는 데이터를 받으면 그대로 쌓는것입니다. 적용후DOM 요소는 제한된 수만 유지data-index에 따라 컨텐츠만 변경기존 DOM을 재활용state가 변경

hongjungkim-dev.tistory.com

 

(이전글) TanStack Query 내부구조 (with 코드) + 실무 적용

https://hongjungkim-dev.tistory.com/manage/posts

 

티스토리

좀 아는 블로거들의 유용한 이야기, 티스토리. 블로그, 포트폴리오, 웹사이트까지 티스토리에서 나를 표현해 보세요.

www.tistory.com

 

(이전글) react-fast-marquee v1.6.5: Duration 계산 불안정으로 인한 속도 불일치 이슈 제기

https://hongjungkim-dev.tistory.com/1365

 

번들 최적화

성과
- FCP(첫 번째 콘텐츠 페인트) 0.9초 → 0.4초 단축
- 번들 크기 2.3MB → 1.1MB 감소 (52% 감소)
- 초기 페이지 로딩 속도 향상으로 운영자가 데이터에 빠르게 접근할 수 있음


1. 라우터, 모달, 드로워 lazy 동적 import
2. Performace탭 분석시, MainContainer에서 echarts를 의존하고 있는 부분이 Message임을 파악하여 정적import로 해당 모듈만 import하도록 변경
3. 3d json폰트를 번들에서 제외

아래 자세히 설명하겠습니다.

번들 결과

parsed 기준(번들된 파일 기준) 단순 비교 25.15MB ⇒ 5.35MB

lighthouse 결과

FCP 0.9s ⇒ 0.4s 단축

최적화 전

 

최적화 후

lighthouse결과

최적화 전

최적화 후

1. code splitting & lazy 동적 import

https://webpack.js.org/guides/code-splitting/

 

Code Splitting | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

Webpack의 Code Splitting 기능은 웹 성능 최적화의 주요 도구로, 번들을 나누어 필요한 리소스만 로드함으로써 초기 로드 속도를 개선하고 앱 응답성을 높입니다. 이를 통해 번들 크기를 줄이고 병렬 로드를 가능하게 하며, 리소스 로드 우선순위를 제어해 전체적인 로드 타임을 최적화합니다. 특히 대규모 애플리케이션에서 사용자 경험 향상에 효과적입니다.

다음의 방법 중에서 Dynamic Imports를 적용하였습니다.

각 페이지 code splitting 적용

// Router.tsx

const SecretHomePage = React.lazy(
  () => import("@pages/SecretHomePage")
);

// 생략

 

 

2. 필요한 모듈만 정적 import

MainContainer에서 echart관련해서 사용한적이 없는데 왜 나올까요?

MainContainer, NavigationContainer에서 다음과 같은 코드를 사용중이었는데요

import { Message } from "@components/dashboard";

이렇게 GuageChart를 import하는 부분이 있어서 echart까지 참조가 된것 같습니다.

import { GaugeChart } from "./data-display";
import {
  Form,
  FormList,
  FormItem,
  InputNumber,
  Select,
  Switch,
  Upload,
  UploadTxt,
  Input,
} from "./data-entry";
import { Space } from "./layout";
import {
  Progress,
  Message,
  Notification,
  PopConfirm,
  Modal,
  FormValidator,
} from "./feedback";
import { Button, Paginate } from "./general";
import { Dropdown } from "./navigation";

export { GaugeChart };
export {
  Form,
  FormList,
  FormItem,
  InputNumber,
  Select,
  Switch,
  Upload,
  UploadTxt,
  Input,
};
export { Space };
export { Progress, Message, Notification, PopConfirm, Modal, FormValidator };
export { Button, Paginate };
export { Dropdown };

그래서 이렇게 직접 해당 Message만 참조하도록 코드를 수정하였습니다.

import { Message } from "@components/dashboard/feedback/message/Message";

lighthouse에는 큰 변화가 없지만 performance탭에서 해당 내용은 제거된것을 확인할 수 있습니다.

3. 3d폰트를 번들에서 제외

번들에서 제외하고 copyPugin으로 서버에 복사하여 서버에 리소스 요청으로 변경하기 위하여 public 경로 하위로 나눔고딕json파일 이동

src/assets/fonts/canvasText/NanumGothic_Bold.json

⇒ public/doms-management/assets/fonts/canvasText/NanumGothic_Bold.json

 

4. 렌더링 과정 중 개선한 단계

개선한 내용이 렌더링 과정중에 어느 단계에 영향을 끼쳤는지 살펴볼 필요가 있어서 그전에 간단하게 렌더링 과정을 정리하겠습니다.

브라우저 렌더링 과정

브라우저가 HTML, CSS, JavaScript 등의 리소스를 받아 화면에 렌더링하는 과정은 다음과 같은 주요 단계로 이루어집니다.

 

1. HTML 파싱 및 DOM 생성

브라우저는 서버에서 받은 HTML을 파싱(parse)하여 DOM(Document Object Model) 트리를 만듭니다.

<script> 태그나 외부 리소스 링크를 만나면 해당 리소스를 다운로드하고 처리합니다.

 

2. CSS 파싱 및 CSSOM 생성

CSS 파일을 다운로드하고 파싱하여 CSSOM(CSS Object Model)을 생성합니다.

CSSOM은 DOM과 결합해 렌더링에 필요한 스타일 정보를 제공합니다.

 

3. 렌더 트리(Render Tree) 생성

DOM과 CSSOM을 결합해 실제로 화면에 그릴 요소들만 포함된 렌더 트리를 만듭니다.

display: none 같은 속성으로 숨겨진 요소는 여기서 제외됩니다.

 

4. 레이아웃(Layout 또는 Reflow)

렌더 트리를 기반으로 각 요소의 크기와 위치를 계산합니다.

화면 크기나 뷰포트에 따라 좌표가 결정됩니다.

 

5. 페인팅(Painting)

계산된 레이아웃을 기반으로 픽셀 단위로 화면에 요소를 그립니다.

텍스처와 그림자 같은 시각적 효과도 이 단계에서 적용됩니다.

 

6. 합성(Compositing)

여러 레이어를 합성해 최종 화면을 완성합니다.

GPU를 활용해 애니메이션이나 스크롤 같은 작업을 효율적으로 처리합니다.

 

그러면 여기에서 어느 단계에 영향을 끼쳤을까요?

 

HTML 파싱 및 DOM 생성 단계에서 번들 크기를 줄인 것이 가장 직접적인 영향을 끼쳤습니다. 불필요한 리소스가 제거됨으로써 브라우저가 HTML 문서를 처리하고 DOM을 생성하는 속도가 빨라지게됩니다. 초기 단계에서 시간을 절약해 후속 단계들이 더 빠르게 진행될 수 있게됩니다.

예를들어, 리소스를 나중에 서버에서 로드되도록 처리했기 때문에 초기 렌더 트리 생성과 레이아웃 계산 과정에서 해당 리소스를 무시하고 단순화된 구조로 작업이 이루어지게 됩니다.

 

정리

번들 최적화로 초기 페이지 FCP 0.9s에서 0.4s로 단축

문제정의

페이지 초기 로드 시 FCP(First Contentful Paint)가 0.9초로 지연되었습니다. webpack bundle analyzer로 분석한 결과, 초기 페이지에서 즉시 필요하지 않은 대용량 리소스와 라이브러리가 함께 로드되어 초기 렌더링을 지연시키는 것을 확인했습니다.

 

해결방법

다음과 같은 최적화 전략을 적용했습니다:

  • 불필요한 번들 분리: 초기 로드에 필요하지 않은 리소스는 번들에서 제외하고 copyPlugin으로 별도 관리
  • 코드 스플리팅: 동적 import와 React.lazy()를 활용하여 필요할 때만 코드를 로드하도록 구현
  • 의존성 최적화: 전체 라이브러리 대신 필요한 모듈만 정적 import하여 번들 크기 감소

개선 결과

  • FCP: 0.9초 → 0.4초 (55.6% 단축)
  • 초기 번들 크기: 2.3MB → 1.1MB (52.2% 감소)
  • 페이지 상호작용 가능 시간(TTI) 개선으로 사용자 경험 향상

'실무 > 성능 최적화' 카테고리의 다른 글

windowing기법과 실무 적용  (0) 2025.03.16

댓글