티스토리 뷰

⏳ 동적 불러오기(Dynamic Import)에 대해 알아보자

🤨 동적 불러오기가 필요한 이유

일반적으로 spa 구조의 어플리케이션을 빌드할 때 js가 하나로 나오며 그 크기가 비대해지는 경험을 해봤을 것이다.

이렇게 되면 브라우저가 처음 index.html을 불러오고 DOM을 그릴때 js 로딩을 기다리는 시간이 길어지므로 FCP 즉 첫 화면이 그려지는데까지 오랜시간이 걸린다. 이는 CSR SPA 구조의 문제점이었다.

그렇다면 하나로 번들링되는 js를 쪼개서 빌드할 수 없을까? 
라는 생각에서 나온 대안 중 하나가 바로 동적 불러오기이다.

동적 불러오기를 적용하게 되면 js를 쪼개서 빌드할 수 있게되고 
이는 브라우저가 DOM을 그릴때 첫화면에 필요한 js만 로드해서 페인팅할 수 있다는 말로 FCP가 앞당겨지며 화면이 빨리 나타나 최종적으로 사용자 경험이 좋아진다.

 

적용전

적용후

 

🎯 적용 방법

일반적인 사용법

간단히 적용할 수 있다.
React이든 Vue든 사용방법은 
const ** = ()=> import('./**')
위와 같은 형식으로 불러오는 파일을 읽어오면 된다.

적용하면 번들러로 무엇을 쓰든 js가 스필릿되어 빌드될 것이다.

보통 가장 많이 사용하는 곳은 라우터이다.
페이지를 로드할때 js 크기를 절감하여 화면을 빨리 그리려는 생각으로
라우터에 적용하게되고 일부 큰 크기가 컴포넌트에도 적용할 경우도 있다.

React에서 사용법

리액트에서는 lazy라는 함수를 사용해 비동기적으로 리액트 컴포넌트를 불러와 리턴해주어 사용하는 경우가 많다.

사용예시

const HomePage = lazy(async () => await import('../pages/HomePage.tsx'));

라우터 사용예시(react router dom v6)

const HomePage = lazy(async () => await import('../pages/HomePage.tsx'));

const router = createBrowserRouter([
    {
      path: '/',
      element: <HomePage />,
    },
  ]);

React Component 파일 내부에서 사용예시

const SomeComponent = lazy(async () => await import('./SomeComponent.tsx'))

function Example(){
	...

	return (
    	<>
        	<SomeComponent/>
        </>
    )
}

default export Example

 

Vue에서 사용법

뷰에서는 라우터에서 사용할때는 따로 전용함수를 사용하지 않아도 되지만
일반적인 vue 파일에서 사용할때는 defineAsyncComponent 함수를 사용해서 불러와야한다.

 

사용예시

const HomePage = async () => await import('../pages/HomePage.vue');

라우터 사용예시(vue router)

const HomePage = async () => await import('../pages/HomePage.vue');

const routes: Array<RouteRecordRaw> = [
    {
      path: '/',
      name: 'home',
      component: HomePage,
    },
  ];
  
const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

Vue 파일 내부에서 사용예시

<template>
...
<SomeComponent/>
...
<template/>

<script setup lang='ts'>
const SomeComponent = defineAsyncComponent(() => import('./SomeComponent.vue'));
<script/>

비동기적 페이지 로드에 Suspense 적용

페이지를 비동기적으로 불러올때 로딩시간이 길어진다면 흰화면을 보게 될 것으로 Suspense를 적용해 fallback UI를 적용하면 좋다.

React

<Suspense fallback={<div>loading...</div>}>
  <RouterProvider router={router} />
</Suspense>

Vue

<Suspense>
  <router-view/>
  <template #fallback>
    Loading...
  </template>
</Suspense>

위처럼 적용하면 각각 loading UI를 적용해두면 느린 네트워크에도 화면이 비지 않고 나오기 좋다. 즉 사용자 경험이 좋아진다.

 

추가적으로 Error UI 처리하기

React는 ErrorBoundary()를
https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary

Vue는 errorCaptured, onErrorCaptured
https://vuejs.org/api/options-lifecycle.html#errorcaptured
https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured

사용해서 Error UI를 구현하고 적용하면 된다.

 

적용예시

<ErrorBoundary>
  <TestPage />
</ErrorBoundary>

 

😶‍🌫️ 적용 시 장단점

장점

  • index.js 용량 감소된것을 확인할 수 있음
  • 각 페이지 별로 이동시 해당 페이지에서 사용하는 js만 불러서 초기 페이지 로드시 도움이 된다
  • 새로고침시 네트워크가 느리더라도 필요 js만 사용하기에 페이지 진입이 빠르다
  • 구버전을 사용하는 사용자 브라우저에서 오류가 있는 버전인 줄 모르고 사용할 수 있는 경우를 방지할 수 있음 (새로고침을 안하고 쭉 사용할 수 있는 경우 예방)

단점

  • 네트워크가 느리면 페이지 이동시 해당 페이지에서 사용하는 js를 다 받기 전까지 페이지 전환이 느릴 수 있다
  • 사용자의 브라우저에서 이전 배포버전에서 새로고침을 해서 js 최신화를 하지 않고 페이지 이동시 해당 이동 페이지에 해당하는 이전 버전 js 파일이 저장소에 존재하지 않아 새로고침을 해주어 다시 리소스를 받아야한다. 
    (이 경우에 새로고침 전에 리소스를 가져오는데 실패했다는 오류 페이지가 잠깐 나오고 새로고침 된다.)
  • 리소스 관리를 달리하여 이전 버전의 js와 새로운 버전의 js를 둘다 가지고 있더라도 자원관리를 지속적으로 스케쥴링해서 삭제 해주어야함

🫡 해결법 및 적용 예시

여러 해결법이 있지만 에러 발생시 자동으로 새로고침하여 빌드된 저장소에서 새롭게 빌드된 파일을 받아오도록 하는 방법을 채택해서 적용했다.

 

리액트 라우터 사용예시

const retryLazy = (componentImport: any) =>
  lazy(async () => {
    const pageAlreadyRefreshed = JSON.parse(window.localStorage.getItem('pageRefreshed') ?? 'false');
    try {
      const component = await componentImport();
      window.localStorage.setItem('pageRefreshed', 'false');
      return component;
    } catch (error) {
      if (!pageAlreadyRefreshed) {
        window.localStorage.setItem('pageRefreshed', 'true');
        return window.location.reload();
      }
      throw error;
    }
  });

const HomePage = retryLazy(async () => await import('../pages/HomePage.tsx'));

const router = createBrowserRouter([
    {
      path: '/',
      element: <HomePage />,
    },
  ]);

Vue도 위와 유사하게 적용하면 된다.

 

참조사이트

https://stackoverflow.com/questions/53704950/webpack-code-splitting-loading-chunk-failed-error-wrong-file-path/62038528#62038528

 

Webpack Code Splitting 'Loading chunk failed' error wrong file path

I'm using React + Typescript with Webpack and I'm trying to load some of my react components when they are actually needed. The issue is that when the chunk is requested via lazy loading I'm gettin...

stackoverflow.com

https://velog.io/@goon126/%EC%B2%AD%ED%81%AC-%EC%97%90%EB%9F%AC

 

[React] 청크 로드 에러 해결하기 (Loading chunk failed)

지난시간에는 코드스플리팅에 대해서 설명했습니다. 허나 무작정 해당 기술을 적용하게되면 이전글에서 설명했다시피 문제가 발생합니다. 바로 배포에서 문제가 발생하는데요. 해서 해당 이슈

velog.io

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함