티스토리 뷰

✅ 현 회사에서 제가 처음 개발부터 유지보수 및 개정사항을 작업을 한 메뉴가 있습니다. 메뉴 초기 개발만해도 2달 정도가 걸린..😓 가장 애착을 가지고 있는 메뉴인데, 현 회사 특성상 매년마다 계산식이나 UI가 바뀜에 따라 점점 크기 자체가 커지고 있었습니다. (현재 4개년의 개정을 함에 따라 크기가 엄청 커졌습니다.) 그래서  운영버전 초기 렌더링이 오래 걸리는 현상이 생겼고 이를 방치하면 년도가 지날수록 더욱 느려질것이 분명하기에 개선해보고자 공부하고 글을 작성합니다. 


🧐 문제 파악

   문제 파악 전 알아야 개념

  • 번들 (Bundle) : 묶는다라는 뜻

    번들링 (Bundling) : 기능별로 모듈화한 js 파일들을 묶어주는 것, 서로 연관(의존성)있는 js 파일들을 하나의 번들 파일로 묶어주는 것

    번들러 (Bundler) : 여러 개로 흩어진 파일들을 압축, 난독화 등을 통해 하나의 파일로 모아주는 도구

  • 청크(Chunk) : 하나의 번들 파일을 효과적으로 다루기 위해 여러가지 파일로 다시 나누는 것 

   문제 파악 ! 

js용량이 커짐에 따라 Chunk.js 파일들의 크기가 커져서 서버로부터 받는데 오래걸렸습니다.

즉, 번들의 크기가 너무 커서 생기는 문제라고 파악했습니다.

 

 ✍  문제 해결 방법

   1. 불필요 코드제거

  • 개발을 하다보면 사용하지 않는 import 나, 로직들이 가끔씩 생깁니다. 저는 일단 이런 불필요한 주석들은 깔끔하게 제거했습니다!

   2. default import 사용해보기 

import {LDialog} from "l-rocket"; // 1.9M (gzipped : 420k)
import LDialog from "l-rocket/LDialog"; // 330.k (gzipped : 92.1k)
  • named export 보다 크기가 훨씬 줄어든 것을 확인 할 수 있습니다.
  • 저는 default import로 바꾸면 개선이 되는 부분들을 모두 바꿔줬습니다!

* 보통 named import가 번들 사이즈 줄이는데 도움이 되지만 default import가 용량측면에서 더 좋은 성능을 가지기도 하는 것 같습니다.

   3. Code Splitting 해보기 

  • 지금 당장 필요한 코드가 아니면 따로 분리시키거나, 나중에 필요할 때 사용하는 것입니다.
  • Dynamic Import(함수 비동기 로딩), React.lazy와 Suspense 사용, Webpack 수정, 크기가 큰 library 대체 등의 방법이 있습니다.

 

/*  Dynamic Import  */

// Before
import { add } from './math';
console.log(add(16, 26));

// After
import("./math").then(math => {
  console.log(math.add(16, 26));
});
  • Dynamic Import, 사용할 함수를 호출할때 동적으로 import()를 하는 방법입니다. 하지만.. 제 메뉴는 무려 비즈니스 로직에 관련된 js파일이 11개나 있고 각 호출빈도가 많아서 Dynamic Import를 하기에는 공수 시간도 오래걸리며, 사이드 이펙트가 생길 우려가 있어서 직접 사용해보진 않았습니다.
  • 같은 이유로 Webpack 수정이나 크기가 큰 library 대체는 하지않았습니다. 따라서 이런 방법들은 먼저 개발 팀장님께 여쭤보고 해야할꺼같습니다 ㅠㅠ

 

/* React.lazy와 Suspense 사용 */

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);
  • React.lazy와 Suspense

    • React.lazy는 동적 import()를 호출하는 함수를 인자로 가지며, React 컴포넌트를 포함하며 default export를 가진 모듈로 결정되는 Promise를 반환합니다.

      lazy 컴포넌트는 Suspense 컴포넌트 하위에 렌더링 되어야 합니다.

      Suspense 는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면을 보여 줄 수 있습니다. (fallback 옵션)

      리엑트 공식 문서 상 라우터에서 사용하면 가장 좋다고 합니다.

      서버 사이드 렌더링에서는 사용할 수 없습니다.

    • 제 메뉴는 SPA 메뉴이기 때문에 메뉴 내 따로 URL 이동이 없어서 라우터에서는 사용하지 못하였고, 대신 처음 렌더링에 불필요한 컴포넌트에 적용시켰습니다.

 

🚨 지금 업무에서 해당 문제를 개선하는 것이 최우선이 아니라 일단 여기까지만 공부해보고, 최적화 될 때까지 계속 이어나가도록 하겠습니다!

 

🔜  [1편] 정리 !

  • 번들의 크기가 커져서 처음 렌더링이 느려지는 현상이 있어 개선이 필요
  • 불필요 코드 제거 + React.lazy와 Suspense를 통해 1차 개선 !
  • 추가적으로 찾아볼것 !

    • 1. 현재 년도에 대한 메뉴 js 파일들만 번들링이 가능한지 ?
    • 2. 지금까지 했던 개선으로 얼마나 속도를 개선했는지 ? 
    • 3. 공통으로 쓰는 부분을 수정했을 때 생길 수 있는 사이드 이펙트가 있는지 ?

 

 

🔗 참고한 글

 

[React]최적화 1편 - 코드 분할

서비스 성능 최적화 시리즈 1편!! 코드 분할 알아봅시다

velog.io

 

리액트 번들 사이즈 최적화

Bundle Size 최적화

roseline.oopy.io

 

댓글