Next

[React][Next.js] 코드 스플리팅(code spliting)과 리액트 서버 컴포넌트(RSC)

leejin_rho 2024. 7. 15. 14:50

컴포넌트 코드들을 처음에 모두 정적으로 import하게 된다면 그 크기는 계속 커지게 된다. 이를 방지하기 위해, 실제 필요한 코드일 경우에만 import를 해줌으로써 번들 크기를 줄이는 방법 ‘코드 스플리팅(code spliting)’이라고 한다.

 

서버 컴포넌트 전에는 React.lazy()를 이용하여 직접 코드를 스플리팅했다. React.lazy()는 컴포넌트를 지연 로딩하는 방법으로, 컴포넌트가 실제로 렌더링되기 전까지는 해당 모듈을 로드하지 않는다. React.lazy()동적 import를 사용하여 필요할 때만 모듈을 로드하며, 이를 통해 초기 로딩 시간을 줄이고, 애플리케이션 성능을 최적화할 수 있다.

 

다음은 서버 컴포넌트 이전 코드의 예시이다.

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

const FirstRenderer = React.lazy(() => import('./FirstRenderer'));
const SecondRenderer = React.lazy(() => import('./SecondRenderer'));

function App() {
  const [showFirst, setShowFirst] = useState(false);
  const [showSecond, setShowSecond] = useState(false);

  return (
    <div>
      <h1>Display</h1>
      <button onClick={() => setShowFirst(!showFirst)}>
        Toggle First Renderer
      </button>
      <button onClick={() => setShowSecond(!showSecond)}>
        Toggle Second Renderer
      </button>
      
      <Suspense fallback={<div>Loading...</div>}>
        {showFirst && <FirstRenderer />}
        {showSecond && <SecondRenderer />}
      </Suspense>
    </div>
  );
}

export default App;

 

동작 설명

  1. 초기 상태: showFirst, showSecond가 false이므로, Suspense 내부의 조건부 렌더링은 아무것도 렌더링하지 않는다.
  2. 버튼 클릭: 각 버튼을 클릭하면 show의 상태가 true로 변경된다.
  3. 조건부 렌더링: show가 true가 되면 <Renderer /> 컴포넌트가 Suspense 내부에서 렌더링된다. ( Suspense 내부에서 컴포넌트가 렌더링되려면 해당 컴포넌트가 React.lazy()로 정의되어 있어야 한다. )
  4. 모듈 로드: <Renderer /> 컴포넌트가 렌더링되면, React.lazy()에 의해 동적 import가 트리거되어 해당 모듈이 비동기적으로 로드된다.
  5. 대체 컨텐츠 표시: 모듈이 로드되는 동안, Suspensefallback으로 지정된 <div>Loading...</div>를 표시한다.
  6. 모듈 로드 완료: 모듈이 성공적으로 로드되면, Suspense는 대체 컨텐츠를 제거하고 실제 <Renderer /> 컴포넌트를 렌더링한다.

하지만 자동 코드 스플리팅을 이용하면 이를 간소화할 수 있다.

 

자동 코드 스플리팅

Next.js 13 이후 리액트 서버 컴포넌트에서는 자동으로 코드 스플리팅을 해준다. 컴포넌트를 import하는 부분을 코드를 나눠야 할 수 있는 부분으로 인식한다. 또한 개발자들이 서버측 코드에서 어떤 컴포넌트를 더 일찍 받아야 하는지 지정할 수 있게 하여 클라이언트 측에서 조금 더 일찍 렌더링 프로세스를 시작할 수 있게 한다.

 

아까의 코드를 리액트 서버 컴포넌트라는 전제하에 수정했다.

import React, { useState } from 'react';
import FirstRenderer from './FirstRenderer';
import SecondRenderer from './SecondRenderer';

function App() {
  const [showFirst, setShowFirst] = useState(false);
  const [showSecond, setShowSecond] = useState(false);

  return (
    <div>
      <h1>Display</h1>
      <button onClick={() => setShowFirst(!showFirst)}>
        Toggle First Renderer
      </button>
      <button onClick={() => setShowSecond(!showSecond)}>
        Toggle Second Renderer
      </button>
      
      {showFirst && <FirstRenderer />}
      {showSecond && <SecondRenderer />}
    </div>
  );
}

export default App;

 

코드가 훨씬 간결해진 것을 확인할 수 있다.

 

따라서 Next.js 13 이후에는 React.lazy()를 사용할 필요없다. 하지만 기본 컴포넌트가 RSC이므로 client component는 'use client' 코드를 작성해 사용해야하는 번거로움은 있다. 보통 큰 페이지들은 server component 로 만들고, 특정 부분은 client component 로 만든다고 한다.