React 최적화 Lazy-loading, Code-splitting, Suspense 프로젝트에 적용해보기(feat. Lighthouse)

kim yeseul·2023년 11월 15일
4
post-thumbnail
post-custom-banner

해당 글은 lazycode-splitting, Suspense을 통한 리액트 성능 최적화에 대한 내용과 실제 프로젝트에 적용해 본 글에 대한 내용입니다.

🏙️React에서 성능 최적화를 하게 된 배경

SPA(Single Page Appliction)의 특성상 맨처음 페이지에 진입하게 되면 웹팩에서 압축한 번들 파일을 다운받게 된다. 즉, 전체 리소스를 한번에 다운받게 된다는 뜻이다.
...
여기서 사용자(클라이언트)는 전체 번들 파일을 다운받기 전 화면을 볼 수 없어 React로 개발된 홈페이지에선 주로 초반에 빈 화면을 보게 될 가능성도 있다. 인터넷 속도가 빠른 한국에서는 큰 문제점을 느끼지 않을 수 있으나 환경이 느린 경우에는 사용자 경험을 저하시키는 요인이 될 수 있다.
...
또한 React 애플리케이션의 규모가 커질수록 메서드, 라이브러리, 유틸리티 크기가 커지게 되고 이는 번들이 커지게 되며 웹 페이지에 방문했을 때 로드해야할 파일의 크기가 커진다는 뜻이고 페이지 성능에 영향을 끼칠 수 있다.

🧐 어떤 기능으로 보완할 수 있나요?

1. Lazy-loading(지연 로딩)

React.lazy는 컴포넌트의 코드가 처음 렌더링될 때까지 로딩을 연기하게 해주는 기능을 한다. 즉, 필요한 시점에 맞추어 컴포넌트를 로딩하므로 애플리케이션의 성능 향상에 기여할 수 있다.
_
React.lazy 메서드는 번들을 여러 chunk로 나눌 수 있게 해주어 사용자에게 보여주기 위한 chunk만 로딩하고 필요에 따라 청크들을 불러오는 것이다. 이는 초기 번들 크기를 줄여준다. 또한 필요한 데이터만 로드하게 되므로 데이터 사용량을 줄이고 또한 사용자에게 빠른 로딩과 더 빠른 상호작용으로 더 나은 사용자 경험을 제공한다.

2. Code-splitting(코드 분할)

사용자(클라이언트)에게 현재 불필요한 코드, 중복되는 코드 없이 적절한 사이즈의 코드가 적절한 타이밍에 동적으로 되도록 하는 것이다.
_
코드를 분할하는 것은 덩치가 큰 번들 파일을 작은 사이즈의 파일로 분할하는 것을 말한다.코드 분할은 코드를 필요에 따라 또는 병렬로 로드할 수 있는 여러 패키지 또는 컴포넌트로 분리하는 것으로 구성된다. 이것은 코드가 필요할 때까지 로드되지 않는다는 것을 의미한다.

Code-splitting의 이점

  • 웹사이트가 콘텐츠를 로드하고 표시하는 속도가 빨라진다.
  • 상호 작용 시간이 향상된다.
  • 웹페이지와 상호작용하지 않고 웹페이지를 떠나는 사용자의 비율이 감소한다.

3. Suspense

컴포넌트가 동적으로 로드되면 어느 순간 아무것도 로드되지 않는 순간이 생기게 된다. 이때 에러를 도출하지 않고 Suspense에 들어오는 컴포넌트를 렌더링 하는 방식이다.
...
Suspense 컴포넌트는 fallback props를 통해 로딩 중일 때의 표시할 JSX를 정의한다.

React.lazy()와 Suspense를 같이 사용하면?

🙆‍♀️ Code splitting과 로딩 중인 컴포넌트를 같이 처리할 수 있다!

React.lazy()를 통해 컴포넌트를 코드 분할할 수 있어 필요한 시점에 컴포넌트를 로드할 수 있고, Suspense는 로딩 중인 컴포넌트를 처리하기 위해 사용한다.

즉, React.lazy()Suspense를 같이 사용하면 초기 번들 크기를 줄이고 애플리케이션 로딩 속도를 개선하며, 로딩 중인 상태를 사용자에게 알려주어 사용자 경험 또한 개선할 수 있다.

실제 프로젝트에 적용해보자

  • 기존 라우팅 코드
import { createBrowserRouter } from "react-router-dom";
import Main from "pages/main";
import MakeScrollToTop from "components/MakeScrollToTop";
// import 생략...

const router = createBrowserRouter([
  {
    element: (
      <>
        <PrivateRouter>
        	<MakeScrollToTop />
        </PrivateRouter>
      </>
    ),
    children: [
      {
        path: "/",
        element: <Main />,
      },
      // 생략
  • React.lazy()Suspense 적용
import { createBrowserRouter } from "react-router-dom";
import React, { lazy, Suspense } from "react";

const Main = lazy(() => import("pages/main"));
const MakeScrollToTop = lazy(() => import("components/MakeScrollToTop"));
// import 생략...(모든 컴포넌트 lazy() 적용)

const router = createBrowserRouter([
  {
    element: (
      <Suspense fallback={<div>Loading...</div>}>
      	<>
      		<PrivateRouter>
      			<MakeScrollToTop />
      		</PrivateRouter>
      	</>
      </Suspense>
    ),
    children: [
      {
        path: "/",
        element: <Main />,
      },
      // 생략

Lighthouse의 Performance의 변화

❌ lazy로 code splitting 전 Reduce unused JavaScript 즉, 사용하지 않는 javscript 문구가 뜬다.

사용하지 않는 javascript의 문제점

사용하지 않는 javascript를 로드하면 대역폭이 불필요하게 증가하고 페이지의 첫 번째 페인트(FCP)가 지연되어 전체 페이지 성능이 느려진다.

⭕ code splitting 후 Reduce unused JavaScript 문구가 사라진 것을 볼 수 있다.

코드 분할을 통해 번들된 자바스크립트를 중요한/중요하지 않은 자바스크립트로 나누어 중요한 자바스크립트 먼저 로드 되도록하여 성능을 최적화할 수 있다.

+) 추가로 performance 점수가 약 4점 가량 올랐다..

profile
출발선 앞의 준비된 마음가짐, 떨림, 설렘을 가진 주니어 개발자
post-custom-banner

0개의 댓글