로딩 성능 최적화 - 코드 스플릿, 레이지 로딩

2023. 4. 28. 17:53·프론트엔드/성능 최적화

Code Split & Lazy Loading

dynamic import나 react-lazy를 사용해서 chunk를 여러 개로 나눠 불필요한 코드, 중복 코드 없이 적절한 크기의 코드가 적절한 타이밍에 로드될 수 있도록 하는 것이다.

하나의 큰 JS파일을 다운로드 하는것 보다 여러개의 작은 chunk(해당 페이지에서 필요한 chunk들만)를 다운로드하는 것이 더 빠르기 때문에 코드 스플릿을 통해 초기 로딩 속도를 개선할 수 있다.

크게 2가지 패턴이 있다.

  • 페이지 별로 (라우팅 단위)
  • 모듈 별로 (컴포넌트 단위)

페이지별로 코드 스플릿

번들 파일 분석하기

코드스플릿을 하기에 앞서 webpack-bundle-analyzer를 활용해서 번들된 파일들의 크기를 확인한다.

보면 refractor라는 모듈이 chunk의 절반을 사용하고 있다.

package.lock을 통해 해당 모듈이 어디서 사용되는지 확인해본다.

react-syntax-highlighter라는 모듈에서 사용하고 있음을 알 수 있다.

해당 모듈은 md의 코드 블럭에서 코드들을 강조처리 하는 역할을 한다.

이 모듈은 상세조회 페이지에서만 사용되기 때문에 목록화면에서는 필요가 없다.

코드 스플릿 하기

페이지 별로 (라우트 기반) 코드 스플릿을 한다.

Suspense, lazy를 사용한다.

Suspense는 해당 페이지에 필요한 파일을 다운로드할 동안 fallback에 제공된 컴포넌트를 보여주고, 다 다운로드하면 원래 보여줘야할 페이지를 보여준다.

lazy는 런타임 중에 필요한 컴포넌트를 레이지 로딩하게 해준다.

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

결과

  • 코드 스플릿 전
  • 코드 스플릿 후

퍼포먼스 탭을 통해 검사해보면, 목록 페이지 로딩이 184.40ms -> 108.27ms로 기존보다 훨씬 빨라진 것을 확인할 수 있다.

번들된 파일을 검사해보면, chunk 가 여러개로 나뉘어져 있는 것을 확인할 수 있다.

각 chunk의 이름이 해시값이라 뭔지 알아보기가 힘들다.

chunk파일 이름 정해주기

웹팩 config에서 chunk파일의 이름을 정해줄 수 있다.

 output: {
    filename: 'static/js/[name].[contenthash:8].js',
    path: path.resolve(__dirname, '../../dist'),
    publicPath: './',
    chunkFilename: 'static/js/[name].[contenthash:8].chunk.js',
  },

chunkFilename에 규칙을 정해줌으로써 각 청크가 무슨 페이지인지 알 수 있다.

컴포넌트 코드 스플릿

번들 파일 분석하기

이미지 모달에서만 사용되는 이미지 갤러리 라이브러리와 이미지모달이 각각 메인 chunk들에 포함되어 있다.

이미지 모달을 눌렀을 때만 사용되므로 해당 컴포넌트를 레이지 로딩하면 chunk의 크기를 줄일 수 있다.

최적화하기

// import ImageModal from './components/ImageModal'

const ImageModal = lazy(() => import('./components/ImageModal'))

결과

이미지 모달을 클릭했을 때 관련 청크들을 불러온다.

번들에서도 코드가 분리된 것을 확인할 수 있다.

이미지 Lazy loading

사이트 분석

이미지를 먼저 다운로드하고 그 후에 배너 비디오를 다운로드한다.

문제점: 배너비디오가 사용자에게 먼저 보여지는데, 다운로드가 늦어서 사용자가 배너 비디오를 늦게 보게 된다.

해결방안:

1) 이미지를 빠르게 다운로드 - 근본적 해결책은 아님
2) 이미지를 나중에 다운로드 (이미지 Lazy loading)

이미지 로드 시점

이미지를 나중에 다운로드한다면 어느 시점에 다운로드해야 될까?

해당 이미지 요소가 보여질 때

IntersectionObserver를 활용해서 구현할 수 있다.
또는 react-lazyload 활용 가능하다.

구현

이미지 주소를 dataset-src에 저장해놨다가 IntersectionObserver를 활용해 해당 이미지가 화면에 보여질 때 src로 dataset-src 를 준다. 이러면 화면에 보여지는 시점에 이미지를 불러온다.

이 때 문제점이 생길 수 있다. width, height을 지정해주지 않으면 src가 없는 img태그는 0x0이라서 한꺼번에 많은 img태그가 한 화면에 있고 동시에 매우 많은 이미지를 다운로드 요청해서 부하가 걸릴 수 있다.

따라서 width,height을 주고 src가 없을 때는 알록달록한 색깔을 주는 식으로 해서 Layout shift도 줄이고 좋은 사용자 경험을 줄 수 있다. ex) Pinterest

/*
 * Image with Lazy Loading
*/
export default function Image(props) {
  const {src, width, height, ...restProps} = props
  const imgRef = useRef()

  useEffect(() => {
    const callback = (entries, observer) => {
      entries.forEach(entry => {
        if(entry.isIntersecting) {
          console.log(entry,'이미지 불러오기!')
          entry.target.src = entry.target.dataset.src
          observer.unobserve(entry.target)
        }
      })
    }
    const observer = new IntersectionObserver(callback, {})
    observer.observe(imgRef.current)
    return () => {
      observer.unobserve(imgRef.current)
    }    
  }, [])

  return <img width={width} height={height} data-src={src} {...restProps} ref={imgRef}/>
}

적용 및 결과

img태그를 Image 컴포넌트로 변경해준다.

적용시 banner-video 로드를 훨씬 빠르게 한다.

저작자표시 (새창열림)

'프론트엔드 > 성능 최적화' 카테고리의 다른 글

로딩 성능 최적화 - 캐시  (0) 2023.04.28
로딩 성능 최적화 - 폰트  (0) 2023.04.28
로딩 성능 최적화 - 프리 로딩  (0) 2023.04.28
로딩 성능 최적화 - 이미지, 동영상  (0) 2023.04.25
웹 성능 최적화 - 기초, 분석 툴  (0) 2023.04.25
'프론트엔드/성능 최적화' 카테고리의 다른 글
  • 로딩 성능 최적화 - 폰트
  • 로딩 성능 최적화 - 프리 로딩
  • 로딩 성능 최적화 - 이미지, 동영상
  • 웹 성능 최적화 - 기초, 분석 툴
정현우12
정현우12
  • 정현우12
    정현우의 개발 블로그
    정현우12
  • 전체
    오늘
    어제
    • 분류 전체보기 (79)
      • 프론트엔드 (56)
        • JavaScript, TypeScript (12)
        • 스타일링 (1)
        • React (13)
        • Next.js (4)
        • 개발 환경 (9)
        • 테스트 (3)
        • 성능 최적화 (11)
        • 함수형 프로그래밍 (2)
        • 구조 (1)
      • 프로젝트 회고 (23)
        • 이미지편집기 개발 (7)
        • 엑셀 다운로드, 업로드 공통 모듈 개발 (4)
        • 사용자 매뉴얼 사이트 개발 (3)
        • 통계자동화 솔루션 개발 (1)
        • 엑셀 편집기 개발 (5)
        • API 플랫폼 (1)
        • 콜센터 솔루션 OB 캠페인 (1)
        • AI 스튜디오 (1)
      • 백엔드 (0)
  • 블로그 메뉴

    • 홈
    • 포트폴리오
    • 태그
  • 인기 글

  • 태그

    TypeScript
    React-boilerplate
    JavaScript
    라이브러리 선정
    Next.js
    memoization
    Github Actions
    webpack
    frontend
    웹 성능 최적화
    엑셀
    로딩 성능 최적화
    사용자 매뉴얼 사이트
    이미지 편집기
    회고
    React
    커스텀 훅
    렌더링 성능 최적화
    useReducer
    엑셀 에디터
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
정현우12
로딩 성능 최적화 - 코드 스플릿, 레이지 로딩
상단으로

티스토리툴바