Skip to content

FrontEnd 성능 개선기

KingDonggyu edited this page Dec 16, 2022 · 3 revisions

작성자 : 김동규

알림 페이지는 진재님이 SSE 작업 중이시니 끝나시면 연동하자..

통계 히스토그램 컴포넌트는 UI 구현했으니 승재님의 API 작업 완료되면 연동하면 되겠고..

그 동안 뭐하지???


라이트 하우스 한번 찍어보자!



성능 측정 항목 대부분의 시간이 너무 오래 걸리는 것을 발견..

사용자가 빠르게 콘텐츠를 인식할 수 있도록 성능 개선의 필요성을 느껴버렸다.

내 성격 상 절대 못지나치지. 개선해보자 🔥


BundleAnalyzerPlugin 웹팩 플러그인을 설치해서 번들 분석을 해봤다.

image



엥.. react-icons 너 뭐야..?

react-icons 라이브러리를 사용하여 애플리케이션에 적용한 아이콘은 단 두가지 밖에 없다.

그런데 쓸데없이 이만큼 차지하고 있다니..


1️⃣ react-icons 라이브러리를 @react-icons/all-files 라이브러리로 대체

react-icons는 icon 종류별로 구분되어 있으며, 종류별로 하나의 js 파일에 아이콘 전체를 포함하고 있다.

  • 따라서 빌드 시 해당 파일이 전체가 포함이 되기 때문에 번들 사이즈가 커지게 된다.

@react-icons/all-files 라이브러리는 아이콘 별로 js 파일을 가지고 있다.

  • 따라서 빌드 시 트리 쉐이킹 방식으로 인해 더 적은 크기의 번들을 만들게 된다.

다시 번들 분석해보자.

image



❗️ node_modules 번들 사이즈가 3.53 MB에서 1.27 MB로 줄었다!!

흠.. 그런데 메인 페이지에서는 사용하지도 않는 chart.js 등 라이브러리가 많은 크기를 차지하고 있다.


2️⃣ React의 lazySuspense를 사용하여 code splitting 적용

각 페이지들을 lazySuspense를 사용해 필요할 때만 불러오도록 했다.

const HomePage = lazy(() => import("@pages/HomePage"));
const ChallengePage = lazy(() => import("@pages/ChallengePage"));
const RecordPage = lazy(() => import("@pages/RecordPage"));
const ProfilePage = lazy(() => import("@pages/ProfilePage"));
const FollowPage = lazy(() => import("@pages/FollowPage"));
const LoginPage = lazy(() => import("@pages/LoginPage"));
const JoinPage = lazy(() => import("@pages/JoinPage"));
const StaticsPage = lazy(() => import("@pages/StaticsPage"));
const SearchPage = lazy(() => import("@pages/SearchPage"));

...

<Suspense fallback={<div>loading..</div>}>
  <Routes>
    {/* Home */}
    <Route path={RoutePath.HOME} element={<HomePage />} />
    <Route path={RoutePath.CHALLENGE} element={<ChallengePage />} />
    <Route path={RoutePath.RECORD} element={<RecordPage />} />
    {/* Search */}
    <Route path={RoutePath.SEARCH} element={<SearchPage />} />
    {/* Statics */}
    <Route path={RoutePath.STATICS} element={<StaticsPage />} />
    {/* Profile */}
    <Route path={RoutePath.PROFILE} element={<ProfilePage />} />
    <Route path={RoutePath.LOGIN} element={<LoginPage />} />
    <Route path={RoutePath.JOIN} element={<JoinPage />} />
    <Route path={RoutePath.SEARCH} element={<SearchPage />} />
    <Route path={RoutePath.FOLLOW} element={<FollowPage />} />
  </Routes>
</Suspense>

...


다시 번들 분석!!




위 번들에서 메인 페이지에 요청되는 번들은 index.js744.js 뿐! 훨씬 가벼워졌다.

❗️ 메인 번들인 index.js의 크기가 307.19 KB에서 10.2 KB로 확연히 줄어든 것을 볼 수 있다.


3️⃣ enum 문법 키워드를 전부 constunion type으로 변경

enum을 사용하면 번들 사이즈가 증가한다.

트리 쉐이킹이 되지 않기 때문이다!! (하지만! 우리가 사용하는 Webpack 번들러에서는 지원이 된다.)

그럼에도 enumconstunion type으로 전부 변경함으로써 번들 사이즈를 줄일 수 있었다.

그 이유는 const + union type의 빌드 코드보다 enum 빌드 코드가 확연히 많기 때문이다.

// constants/enum.ts

export enum RoutePath {
  HOME = "/",
  STATICS = "/statics",
  SEARCH = "/search",
  PROFILE = "/profile",
  CHALLENGE = "/challenge",
  RECORD = "/record",
  LOGIN = "/login",
  JOIN = "/join",
  FOLLOW = "/follow",
};

...

이랬던 코드를 아래와 같이!

// constants/enum.ts

export const RoutePath = {
  HOME: "/",
  STATICS: "/statics",
  SEARCH: "/search",
  PROFILE: "/profile",
  CHALLENGE: "/challenge",
  RECORD: "/record",
  LOGIN: "/login",
  JOIN: "/join",
  FOLLOW: "/follow",
} as const;
export type RoutePath = typeof RoutePath[keyof typeof RoutePath];

...

그 결과..



❗️ 메인 번들(index.js) 사이즈가 10.2 KB에서 7.79 KB로 줄었다!


4️⃣ 이미지 최적화

그럼 이 기세를 이어 이미지도 최적화해보자.

JPG, PNG 등 이미지 파일들을 WebP 파일로 변환하여 크기를 줄였다.

  • PIXLR 사이트에서 쉽게 변환할 수 있다.
  • 그래서 전부 수동으로 변경했다..

🏆 최종 결과



👍 전제 번들 사이즈가 3.83 MB에서 1.68 MB로 줄어들었다.

👍 홈 페이지 기준 메인 번들(index.js)이 307.14 KB에서 7.79 KB로 줄어들었다.

👍 Speed Index가 7.9 초에서 0.6 초로 줄어들었다.

👍 Total Blocking Time이 240 밀리초에서 130 밀리초로 줄어들었다.


아쉬운 점

Time to Interactive, Largest Contentful Paint 성능 요소는 크게 줄이지 못했다.

한번에 여러 데이터를 다른 API를 통해 각각 응답받기 때문에, 서버에서의 모든 데이터 응답 시간이 느려지는 요인이 큰 것 같다.

이 부분을 제외하고도 여러 문제점들을 분석하고 개선해보자! 🔥

그리고 현재는 홈 페이지에 대해서만 성능 분석 및 개선을 수행했다.

다른 페이지들에 대해서도 얼른 해보고 싶다.

💻 Projects

🤝 Rules

🎙️ Meeting

👾 Trouble Shootings

🛠 Tech Semina

🔰 초심자를 위한 기술 가이드

🏃‍♂️ Sprint

✏️ Reviews

💎 Mentoring

💬 Scrums

Week 1
Week 2
Week 3
Week 4
Week 5
Week 6
Clone this wiki locally