본문 바로가기
개발/React

React : code spliting을 이용한 성능 최적화

by 안뇽! 2023. 5. 18.
반응형

code spliting을 이용한 성능 최적화

React는 일정 규모에 도달하면 파일 크기가 커지고 이는 웹사이트의 초기 로딩 시간에 부정적인 영향을 미친다. 이 문제를 해결하기 위해 React 는 code spliting 이라는 기법을 제공한다. code spliting 기법을 사용하면 필요한 코드만 로딩하는데 도움이 되어 웹사이트의 성능을 향상시킬 수 있다.

프론트엔드 성능 최적화 가이드
프론트엔드 성능 최적화 가이드

프론트엔드 성능 최적화 가이드:웹 개발 스킬을 한 단계 높여 주는, 인사이트

동료들과 위 책으로 스터디를 하는데, suspense를 이용하여 code spliting을 통한 성능 최적화를 할 수 있다는 내용을 보았다.

예전에 부트캠프에서 공부할때 공식문서에서 보았던게 생각났다. 어느새 잊고있었군

Code Spliting (코드분할)

페이지에서 필요한 코드만 따로 로드하면 불필요한 코드를 로드하지 않아도 된다.

예를들어서 마이페이지에서만 쓰는 코드를 메인페이지에서 로드하는 것은 불필요한 코드를 로드하는 것이다.

이는 불필요하게 로딩을 지연시킨다.

 

Code Spliting 기법은 페이지 별로 코드를 분리할 수 있게 한다.

분리한 코드는 사용자가 서비스를 이용하는 중 해당 코드가 필요해지는 시점에 로드되어 실행된다. 이를 지연로딩이라고 한다.

 

1. dynamic import

// import 기본 사용법

import { add } from './math'

console.log('1+4 = ',add(1+4));

위와 같이 작성하면 해당 모듈은 빌드시에 함께 번들링되어, 페이지가 로딩되는 chunk안에 포함된다.

하지만 아래의 dynamic import로 작성된 파일은 빌드할 때가 아닌 런타임에 해당 모듈을 로드하게 된다.

// dynamic import

import('add').then((module)=> { 
  const { add } = module
  console.log('1+4 =',add(1+4))
})

webpack은 이 동적 import 구문을 만나면 코드를 분할하여 번들링한다. 하지만 이 dynamic import는 Promise형태로 모듈을 반환하기 때문에 Promise 내부에서 로드된 컴포넌트를 Promise 밖으로 빼주어야 하는 문제가 있다.

 

다행히 리액트에서는 이런 문제를 해결하기 위한 메소드가 2개 있다.

2. Suspense 와 lazy

React.lazy() 와 Suspense를 이용해 동적 import를 사용하여 code spliting을 구현할 수 있다.

다음 예시는 페이지 별로 코드를 분할하기 위해 Router쪽에서 Code Spliting을 했다.

import React, { Suspense, lazy } from "react";
import { Switch, Route } from "react-router-dom";
import "./App.css";
// import ListPage from './pages/ListPage/index'
// import ViewPage from './pages/ViewPage/index'

const ListPage = lazy(() => import("./pages/ListPage"));
const ViewPage = lazy(() => import("./pages/ViewPage"));
function App() {
  return (
    <div className="App">
      <Suspense fallback={<div>loading...</div>}>
        <Switch>
          <Route path="/" component={ListPage} exact />
          <Route path="/view/:id" component={ViewPage} exact />
        </Switch>
      </Suspense>
    </div>
  );
}

export default App;

React.lazy()는 동적 import를 사용하여 모듈을 로드하는 함수를 인수로 받는다. 이 함수는 Promise를 반환하며, 이 Promise는 export를 default로 가진 모듈 객체를 반환해야 한다. Suspense는 우리가 로드 중인 컴포넌트를 래핑하며 컴포넌트가 로드될 때까지 일종의 백업 UI를 제공한다. fallback prop은 컴포넌트가 로딩중일때 보여줄 React element이다.

 

위와 같이 작성하면 각 페이지 컴포넌트는 코드가 분할되고 사용자가 목록 페이지에 접근했을 때 전체 코드가 아닌 ListPage 컴포넌트의 코드만 dynamic import 하게 된다. Suspense로 dynamic import한 컴포넌트를 감싸주지  않으면 컴포넌트가 제대로 로드되지 않는 순간이 생기기 때문에 꼭 Suspense를 사용해주어야 한다.

code spliting 실습

이 실습은 위에서 소개한 책의 예제 파일로 진행했다.

 

1. App.js파일에서 Code Spliting을 진행하지 않았을때

import React from "react";
import { Switch, Route } from "react-router-dom";
import "./App.css";
import ListPage from "./pages/ListPage/index";
import ViewPage from "./pages/ViewPage/index";

function App() {
  return (
    <div className="App">
      <Switch>
        <Route path="/" component={ListPage} exact />
        <Route path="/view/:id" component={ViewPage} exact />
      </Switch>
    </div>
  );
}

export default App;

 

code spliting을 하지 않았을 때 1.chunk.js 다운로드 시간
1.chunk.js 다운로드 시간이 129.59 ms

1.chunk.js의 다운로드 시간이 129.59ms이다.

 

 

2. Code Spliting을 했을때

 

import React, { Suspense, lazy } from "react";
import { Switch, Route } from "react-router-dom";
import "./App.css";
// import ListPage from './pages/ListPage/index'
// import ViewPage from './pages/ViewPage/index'

const ListPage = lazy(() => import("./pages/ListPage/index"));
const ViewPage = lazy(() => import("./pages/ViewPage/index"));
function App() {
  return (
    <div className="App">
      <Suspense fallback={<div>loading...</div>}>
        <Switch>
          <Route path="/" component={ListPage} exact />
          <Route path="/view/:id" component={ViewPage} exact />
        </Switch>
      </Suspense>
    </div>
  );
}

export default App;

code spliting을 했을때 1.chunk.js 다운로드 시간
chunk.js의 다운로드 속도가 129.59ms에서 73.89ms 로 감소하였다

chunk.js의 다운로드 속도가 129.59ms에서 73.89ms 로 감소하였다.

 

실제 번들링도 변화한 것을 볼 수 있다.

 

before,after를 비교해보았다.

code spliting을 하지 않았을 때 번들링
before

아래 이미지인 after에서는 Axios(외부에서 받아오는 페이지)가 들어간 파일은 또다른 chunk로 분리된 것을 포함하여 before에 비해 chunk파일이 5개로 분리된 것을 확인할 수 있다.

code spliting을 했을때 번들링
after

네트워크 탭에서도 bundle들이 분리되어 다운로드 되는 것을 확인 할 수 있다.

 

역시 before,after를 비교해보았다.

code spliting을 하지 않았을때 네트워크탭
before

before에서는 모든 파일을 3.chunk.js안에 모두 포함시켜 6.38s동안 다운로드하지만,

 

after에서는 0.chunk.js, 1.chunk.js, 2.chunk.js, 4.chunk.js, 5.chunk.js로 순차적으로 다운로드 받는다.

 

code spliting을 했을때 네트워크탭
after

 


이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

반응형