Please enable JavaScript to view the comments powered by Disqus.

dynamic import로 줄여본 next.js 앱의 최초 로딩 시간

dynamic import

Next.js는 ES2020의 dynamic import 문법을 지원한다. dynamic import를 사용하면 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 이를 통해 번들 파일을 분리하고 퍼포먼스 향상을 기대할 수 있다. 앱에는 초기 로딩부터 사용하지 않는 부분이 존재할 수 있으며, 또 그 부분의 사이즈가 생각보다 클 수 있기 때문이다. 그리고 사용자 언어 등 런타임에서만 알 수 있는 정보에 기반해서 모듈을 선택해야 하는 케이스도 생길 수 있다.

dynamic import의 공식 문법에서는 import 호출이 promise를 리턴하는 것처럼 코드를 작성한다.

// this code resides in add.js file
const add = (a, b) => a + b;
export default add

// in index.js file
import('./add.js')
  .then(module => module.default(7, 4)) // returns 11
  .catch(error => // log error);

하지만 next.js에서는 dynamic이라는 모듈을 제공해서 promise resolve 과정 없이도 변수에 할당할 수 있도록 해준다.

import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(() => import('../components/hello'))

next.js 프로젝트에 적용하기

작업 중인 프로젝트에서는 각각의 라우트에 매칭되는 루트 컴포넌트를 하나씩 만들었다. 라우트의 컨텐츠는 루트 컴포넌트 안에 들어간다. 따라서 특정 라우트에만 사용되는 컴포넌트가 여럿 존재하게 된다. 그리고 next.js로 작성된 앱은 서버 렌더링을 하기 때문에 라우트에 매칭되는 컴포넌트가 아닌 다른 루트 컴포넌트들을 모두 불러올 필요가 없다. 그래서 next/dynamic 모듈을 사용해서 그 루트 컴포넌트들을 불러오도록 했다.

// index.js
import React from 'react'
import dynamic from 'next/dynamic'

// index 라우트에서 사용할 루트 컴포넌트(HomePage)를 dynamic import로 불러온다.
const Home = dynamic(() => import('components/pages/HomePage')) 

class index extends React.Component {
  render() {
    return <Home></Home>
  }
}

export default index

dynamic import 적용 전의 빌드 결과

before_dynamic_import_page_components

dynamic import 적용 후의 빌드 결과

after_dynamic_import_page_components

모든 라우트에 dynamic import를 적용하고 빌드를 해 보니 first load 사이즈가 크게 줄었다. 페이지별 번들 파일의 사이즈가 300kb대에서 100kb 단위로 줄어든 것을 확인할 수 있다. 하지만 share by all 항목에 있는 파일의 수가 더 증가했다.

dynamic import 전후의 로딩 시간 비교

다이나믹 임포트 전의 로딩 워터폴 그래프

before_dynamic_-_file_load_waterfall

자바스크립트 파일의 로딩은 200ms 전후로 모두 끝이 난다.

다이나믹 임포트 후의 로딩 워터폴 그래프

after_dynamic_-_file_load_waterfall

번들 사이즈가 줄어들어서 200ms 훨씬 전에 로딩이 끝난다

정리

테스트에 사용한 엡은 라우트가 6개밖에 되지 않으므로 사이즈가 작은 앱이라고 할 수 있다. 만약 앱의 규모가 이보다 크다면 dynamic import로 얻을 수 있는 효과도 더 커질 것으로 보인다.