코드 스플리팅

IT공부중·2021년 2월 22일
0

frontend

목록 보기
8/16

요즘 개발 환경에서 번들링을 이용해서 모든 코드를 하나의 번들로 묶어서 만들게 된다. 앱이 비교적 간단한 편이라면 크게 문제가 되지 않지만, 프로젝트가 커지면 전달해야 하는 파일도 커지고, 유저의 브라우저가 파싱해야하는 정보도 많아지기 때문에 퍼포먼스 문제들이 생길 수 밖에 없다.

code splitting을 하면 사용자가 현재 필요로하는 것들만 lazy-load 할 수 있으므로 앱의 성능을 크게 향상 시킬 수 있다. 앱의 전체 코드양을 줄이지는 않지만 사용자가 필요로 하지 않는 코드를 로드 하는 것을 피하고, 초기 페이지 로드시 필요한 코드만 받게 된다.

개발자로서의 목표는, 유저가 당장 필요한 정보에 우선 순위들 두어 순서대로 로딩하는 것이다. 앱에서 아직 정보를 로딩 중이더라도, 유저한테 티가 안 나면 결국 어느정도 좋은 UX를 전달 할 수 있다.

Webpack은 자바스크립트에서 코드스플리팅을 할 때 가장 흔하게 쓰인다. Webpack으로 코드스플리팅을 하는 방법은 여러가지이지만, 그 중 하나인 dynamic import의 예시를 본다.

if(videoComponent) {
  import('./component/video')
    .then(video => {
        video.loadElements()
    })
    .catch(e => console.log(e))
}

원래 방식대로 import를 하지 않고 원하는 함수 형태로 써서 필요할 때만 import를 한 뒤, 돌아오는 promise에서 모듈을 실행할 수 있도록 만드는 것이다. 만약 video 라는 컴포넌트가 로딩하는데에 오랜 시간이 걸린다면, 유저가 사용하기 전까지는 import를 안 해주는게 낫다.

dynamic import를 React에서 활용할 수 있는 방법을 알아본다.

React.lazy()

리액트 라이브러리에 컴포넌트 레벨 dynamic import를 위한 함수가 내장되어 있다.

import React, { lazy } from 'react';

const NewPopup = lazy(() => import('./views/NewPopup'));

const MainComponent = () => (
  <>
    <NewPopup />
  </>
)

이렇게 import를 lazy라는 함수 안에 쓰고, Suspense라는 컴포넌트로 관리 할 수 있다.

React.lazy는 SSR이 안 되기 때문에 loadable-components 라이브러리를 사용하라고 권고함.

React.lazy는 export default만 지원해서 named export를 사용하면 default로 이름을 재정의 하는 중간 모듈을 생성할 수 있다.

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

Suspense

Dynamic import를 할 때마다 새로운 데이터가 로딩되는 도중에 유저에게는 앱에서 코드가 비어있는 컴포넌트가 보인다. 이럴 때 로딩 중이라는 컴포넌트를 보여주면 UX 향상이 있을 것이다. 이것을 React.lazy와 Suspense로 쉽게 관리 할 수 있다.

import React, { lazy, Suspense } from 'react';

const NewPopup = lazy(() => import('./views/NewPopup'));

const Loading = () => <p>Loading</p>;

const MainComponent = () => (
  <Suspense fallback={<Loading/>}>
    <NewPopup />
  </Suspense>
)

이렇게 dynamic loading으로 split 하는 경우는 주로 3가지이다.

  1. Route level: 각 react-router 마다 dynamic loading을 설정해놓기

Splitting을 할 곳을 가장 찾기 쉬운 방법이다. 각 라우트가 다른 컴포넌트로 관리를 하고 있을 경우, 각 라우트를 import 함수를 통해 분리된 빌드 파일로 관리를 할 수 있다. 유저가 다른 페이지로 넘어가 때에만 그 페이지를 asynchronouse 하게 로딩할 수 있다.

  1. Component level: 유저의 input으로 인해 나타나는 컴포넌트

페이지가 처음으로 로딩이 되었을 때, 그 페이지 안에 있지만 보이지 않는 컴포넌트가 존재할 수 있다. 예를 들어, 유저가 이메일 페이지에서 새로운 메일을 작성하고자 할 때, 작성하기 버튼을 눌러 모달이 뜨게 된다면 그 모달을 import()로 스플리팅해서 관리할 수 있다.

  1. 하나의 페이지를 스플리팅 하기

페이지 하나가 되게 긴 경우, 그 페이지에 들어갈 때 당장 보이는 부분을 나머지아 분리하고, 그 뒷부분을 다른 컴포넌트로 만들어 스플리팅 할 수 있다.

히어로 엘리먼트를 먼저 불러오게 하고 다른 쪽 코드들은 나중에 불러오게 하는 방법을 사용할 수 있을 것 같다. 또한, 화면의 밖에 있는 부분은, IntersectionObserver 등을 사용해서 늦게 불러오게 할 수 있지 않을까?

참고

https://velog.io/@odini/Code-Splitting코드-스플릿팅

https://medium.com/humanscape-tech/react에서-해보는-코드-스플리팅-code-splitting-56c9c7a1baa4

profile
3년차 프론트엔드 개발자 문건우입니다.

0개의 댓글