React Lazy (Code Splitting)

Franklee·2023년 3월 3일
0

React

목록 보기
3/4
post-thumbnail

코드분할

Lazy, Suspense 등의 리액트 관련 이야기를 하기전에 '코드분할'에 대해서 알고 넘어가자.

코드 분할이란 받아오는 파일을 분할하여 필요시에 해당 파일을 다운받게 하는 방법이다.

웹페이시 진입시 해당 페이지의 파일을 받아오게 되는데, 파일의 크기가 크면 클수록 로딩시간이 오래 걸리게 되고 이는 사용자 경험을 나쁘게 만드는 핵심요소중 하나이다. 실제 사용자의 50%이상이 로드하는데 3초 이상 걸리는 경우 웹사이트를 이탈한다고 하기 때문에, 첫 로드 및 데이터를 표시하는데 있어 로드시간을 단축하는것은 매우 중요하다.

결과적으로, 대용량의 JavaScript를 다운로드 하는것은 사이트 속도에 굉장히 큰 영향을 끼치기 때문에 코드 분할을 통해 대용량의 파일을 분할하여 필요시에 다운로드 하는 방식으로 사용자 경험을 개선하고자 하는 것이다.


Lazy

React.lazy 메서드를 사용하면 동적 불러오기가 가능해지며, 사용 방법도 비교적 간단하다. React에서는 컴포넌트별로 작성하기에 설정도 컴포넌트 별로 설정하게 된다.

const LazySomething =lazy(()=>import('./Something'));
import 하고자 하는 컴포넌트에 lazy적용

<LazySomething/>
일반 컴포넌트와 같이 사용


Susepense

Suspense는 lazy를 로드할때 결과적으로 서버에서 다운로드를 받는 것이기 때문에 짧은 시간이라도 로딩되는 순간이 존재하기에 해당 시간에 유용한 로드 상태를 표시하기위한 요소이다.

Suspense에는 fallback이라는 attribute가 존재하며, 개발자에 의해 할당된 컴포넌트 혹은 문자열이 lazy로드 시간동안 보여지게 되는 것이다.

import React, { lazy } from 'react';

const LazySomething =lazy(()=>import('./Something'));
//lazy import

function App(){
  return(
    <div>
    	<Suspense fallback={<Loading Spinner/>}
    		<LazySomething/>//lazy 컴포넌트 불러오기
        </Susepense>
    </div>
  )
}

실제로 코드 분할이 되고 있는지에 대해서는 브라우저의 개발자 도구에서 네트워크 탭에서 최초 로드시에 lazy를 설정하지 않은 chunk.js가 다운로드 될 것이고, lazy를 설정한 (위에서는 LazySomething) 컴포넌트가 상황에 의해서 필요하게 된다면 또다른 chunk.js를 다운 받는 것을 볼 수 있을 것이다.


해당 이미지는 어느 사이트 최초 로드시 그리고 lazy를 사용한 분할 코드 다운로드를 보여준다.

다섯번째 main.chunk.js까지가 최초 로드시에 다운받은 파일이다.
(페이지의 환경 및 모든 요소를 제외하고 예시로만 보자)
이는 이미지의 오른쪽에서 처음5개의 파일은 비슷한 위치에 막대가 있으므로 최초로딩, 이후 4개의 chunk.js는 필요시에(사용자 행동에 의해) lazy로딩된 부분이다.


ErrorBoundary

Suspense가 로드시간동안의 로드 상태 표시를 위함이었다면,
ErrorBoundary는 로드의 오류를 감지하여 lazy로드가 어떠한 이유로 실패 시 실패에 대한 메시지 등을 보여줄 수 있도록 하는 기능입니다.

예를 들어, 버튼클릭시 사용자에 대한 정보를 가진 컴포넌트를 lazy로드를 하는 기능이 있다고 하자. 정상적으로 사용자 정보를 가져오게 된다면 컴포넌트는 정상적으로 랜더링이 될것이지만, 사용자 정보를 가져오는 부분에서 어떠한 오류가 발생해 해당 컴포넌트를 정상적으로 랜더링 하지 못하게 된다면 ErrorBoundary에서 이러한 오류를 감지해 오류 메시지(컴포넌트)를 사용자에게 보여줄 수 있는 것이다.

ErrorBoundary는 직접 구현해야 하는 기능이지만, 리서치를 하면 매우 간단하게 예시를 볼 수 있다.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
    //기본 상태값 {에러있니:아니요}
  }

  static getDerivedStateFromError(error) {
    //에러가 있다면 상태값은 {에러있니:네}
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      //에러가 있다면 아래 엘리먼트를 리턴
      return <p>Loading failed! Please reload.</p>;
    }
    return this.props.children;
    //리턴
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>//에러 바운더리는 suspense보다 상위에 위치
    <Suspense fallback={renderLoader()}>//로딩상태표시
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

코드 출처

(함수형 작성법에 익숙한 나로서는 클래스형태는 귀찮지만, 모든 리액트가 클래스로 작성된 것을 아니기에 이런 기회를 통해 클래스작성법에 익숙해지는 계기를 가지자...)

코드 출처에서 초심자를 위해 친철하게 작성한 결론을 끝으로 포스팅을 끝낸다.

결론

React 애플리케이션에 코드 분할을 적용할 위치가 확실하지 않은 경우 다음 단계를 따르세요.

  1. 경로 수준에서 시작합니다. 경로는 분할할 수 있는 애플리케이션의 지점을 식별하는 가장 간단한 방법입니다. React 문서에 Suspense를 react-router와 함께 사용하는 방법이 설명되어 있습니다.

  2. 특정 사용자 상호작용(예: 버튼 클릭)에서만 렌더링되는 사이트 페이지의 큰 구성요소를 식별합니다. 이러한 구성 요소를 분할하면 JavaScript 페이로드가 최소화됩니다.

  3. 화면 밖에 있고 사용자에게 중요하지 않은 것은 모두 분할하는 것을 고려하십시오.

profile
Frontend Dev

0개의 댓글