Dynamic Import / Code Code Splitting

이윤희·2025년 2월 25일

React/Next 일기

목록 보기
47/52

다이나믹 로딩으로 코드 스플리팅을 해서 성능을 향상시킨다는데 무슨 말인지 모르겠음!!!
그래서 다이나믹 로딩이 뭔지 부터 물어봤다...

GPT

📌 Next.js의 다이나믹 로딩(Dynamic Import)이란?

다이나믹 로딩(Dynamic Import)이란 필요한 순간에만 특정 코드나 컴포넌트를 로드하는 기술입니다.
Next.js에서는 import()next/dynamic을 사용하여 동적 로딩을 구현할 수 있습니다.

💡 왜 Dynamic Import를 사용할까요?

  • 초기 로딩 속도 개선: 처음 페이지를 불러올 때 불필요한 코드까지 함께 로드하면 속도가 느려짐.
  • 번들 크기 감소: 코드 스플리팅(Code Splitting)을 통해 필요한 코드만 불러옴.
  • 서버 사이드 렌더링(SSR) 비활성화 가능: 클라이언트에서만 실행해야 하는 코드(예: 브라우저 전용 라이브러리)를 다룰 수 있음.

📌 Dynamic Import 사용법

Next.js에서는 next/dynamic을 사용해 다이나믹 로딩을 쉽게 구현할 수 있습니다.

✅ 1. 기본적인 Dynamic Import

import dynamic from "next/dynamic";

// 'HeavyComponent'를 동적으로 로드
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"));

export default function Home() {
  return (
    <div>
      <h1>Welcome to My Portfolio</h1>
      <HeavyComponent />
    </div>
  );
}

📌 설명

  • import("../components/HeavyComponent")HeavyComponent.js 파일을 필요할 때만 로드함.
  • 즉, Home 페이지가 처음 로드될 때 HeavyComponent는 로딩되지 않으며, 실제로 필요한 순간에만 불러옴.

✅ 2. 서버 사이드 렌더링(SSR) 비활성화

일부 컴포넌트는 클라이언트에서만 실행되어야 합니다.
예를 들어, 브라우저 전용 라이브러리(로컬 스토리지, 창 크기 감지 등)를 사용하는 경우 SSR을 비활성화해야 합니다.

import dynamic from "next/dynamic";

// SSR을 비활성화하여 클라이언트에서만 로드되도록 설정
const NoSSRComponent = dynamic(() => import("../components/NoSSRComponent"), {
  ssr: false,
});

export default function Home() {
  return (
    <div>
      <h1>Welcome to My Portfolio</h1>
      <NoSSRComponent />
    </div>
  );
}

📌 설명

  • { ssr: false } 옵션을 추가하면 서버에서 이 컴포넌트를 렌더링하지 않고, 클라이언트에서만 로드합니다.
  • 이 방법은 브라우저 환경에서만 동작해야 하는 라이브러리(예: localStorage, window 객체 등)를 사용할 때 유용합니다.

✅ 3. 로딩 상태 표시 (Loading Component 추가)

다이나믹 로딩 시 로딩 화면(Loading Component)을 추가하여 사용자 경험을 개선할 수 있습니다.

import dynamic from "next/dynamic";

// 'Loading...'을 표시하는 로딩 컴포넌트 추가
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
  loading: () => <p>Loading...</p>,
});

export default function Home() {
  return (
    <div>
      <h1>Welcome to My Portfolio</h1>
      <HeavyComponent />
    </div>
  );
}

📌 설명

  • loading: () => <p>Loading...</p> 옵션을 추가하면 컴포넌트가 로드될 때까지 "Loading..." 문구가 표시됨.

✅ 4. 여러 개의 Dynamic Import 적용

다양한 컴포넌트를 각각 필요할 때만 로드하도록 설정할 수도 있습니다.

import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("../components/HeavyChart"), { ssr: false });
const HeavyMap = dynamic(() => import("../components/HeavyMap"), { loading: () => <p>Loading Map...</p> });

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <HeavyChart />
      <HeavyMap />
    </div>
  );
}

📌 설명

  • HeavyChartSSR을 비활성화하고, 클라이언트에서만 로드되도록 설정.
  • HeavyMap로딩 상태를 추가하여 지도 로드 시간이 길어도 사용자에게 피드백 제공.

📌 Dynamic Import의 장점과 단점

✅ 장점

  1. 초기 로딩 속도 개선
    • 필요할 때만 불러오기 때문에 초기 번들 크기를 줄일 수 있음.
  2. 번들 크기 최적화 (Code Splitting)
    • React.lazy()와 유사한 방식으로 동작하여 불필요한 코드 로딩을 방지.
  3. SSR 비활성화 가능
    • 특정 컴포넌트를 클라이언트에서만 실행하도록 설정 가능.

❌ 단점

  1. 초기 로딩 시 깜빡임(Flickering) 가능
    • 동적 로딩되는 동안 컴포넌트가 보이지 않을 수 있음 → loading 옵션으로 해결 가능.
  2. 컴포넌트가 많아지면 코드 관리 어려움
    • Dynamic Import를 너무 많이 사용하면 유지보수하기 어려울 수도 있음.

📌 정리

기능코드 예제설명
기본 Dynamic Importdynamic(() => import("../components/HeavyComponent"))필요할 때만 로드
SSR 비활성화{ ssr: false }브라우저 전용 코드에 사용
로딩 상태 표시{ loading: () => <p>Loading...</p> }로딩 화면 추가
여러 개의 Dynamic Importdynamic(() => import("../components/HeavyChart"))각 컴포넌트를 동적 로딩

📌 언제 Dynamic Import를 사용해야 할까요?

적용하면 좋은 경우

  • 크기가 큰 차트, 지도, 동영상 등 무거운 컴포넌트가 있을 때
  • 브라우저에서만 동작해야 하는 기능 (window, localStorage 사용)
  • 자주 사용되지 않는 페이지(예: 관리자 페이지 등)

🚫 사용을 피해야 할 경우

  • 모든 페이지에서 항상 필요한 컴포넌트 (예: 네비게이션 바, 헤더 등)
  • 너무 많은 동적 로딩을 사용하면 코드 복잡도가 증가

📌 코드 스플리팅(Code Splitting)이란?

코드 스플리팅(Code Splitting)은 웹 애플리케이션의 JavaScript 번들을 작은 조각들로 나누어, 필요한 순간에만 로드하는 최적화 기법입니다.

즉, 한 번에 모든 코드를 불러오는 것이 아니라, 실제로 필요한 코드만 로딩하여 성능을 개선하는 방법입니다.


🤔 왜 코드 스플리팅이 필요할까요?

웹사이트를 처음 로드할 때 모든 JavaScript 파일을 한 번에 다운로드하면, 다음과 같은 문제가 발생합니다.

문제점
1. 초기 로딩 속도가 느려짐 → 사용자 경험(UX)이 나빠짐
2. 사용하지 않는 코드도 함께 로드됨 → 불필요한 리소스 낭비
3. 페이지가 무거워짐 → 성능 저하

📌 해결 방법: 코드 스플리팅!

  • 앱이 실행될 때 모든 코드를 한 번에 로드하는 것이 아니라, 필요한 시점에 필요한 코드만 가져오는 방식.
  • 이렇게 하면 초기 로딩 속도를 개선하고, 성능을 최적화할 수 있음.

✅ 코드 스플리팅의 3가지 방식

코드 스플리팅은 여러 방법으로 구현할 수 있습니다.

방법설명예제
1. 페이지 단위 코드 스플리팅페이지마다 코드를 분리하여, 특정 페이지에 들어갈 때만 로드Next.js의 자동 코드 스플리팅
2. 컴포넌트 단위 코드 스플리팅무거운 컴포넌트를 동적으로 가져와서 필요한 순간에 로드React.lazy(), dynamic import()
3. 라이브러리 코드 스플리팅사용하지 않는 라이브러리를 한꺼번에 로드하지 않고, 필요한 시점에 로드webpacksplitChunks 옵션

✅ 1. 페이지 단위 코드 스플리팅 (Next.js의 자동 코드 스플리팅)

Next.js는 기본적으로 각각의 페이지를 별도의 JavaScript 파일로 분리하여 자동으로 코드 스플리팅을 수행합니다.

/pages
  ├── index.js       → 첫 화면을 위한 코드만 로드됨
  ├── about.js       → "About" 페이지에 들어갈 때만 로드됨
  ├── contact.js     → "Contact" 페이지에 들어갈 때만 로드됨

📌 설명

  • index.js 페이지에 있을 때는 about.js, contact.js 코드를 로드하지 않음.
  • 사용자가 about 페이지로 이동하면, 그때서야 about.js 파일을 로드함 → 초기 로딩 속도 개선!

📌 즉, Next.js를 사용하면 페이지별로 자동으로 코드가 분리됨!


✅ 2. 컴포넌트 단위 코드 스플리팅 (React.lazy & dynamic import)

페이지가 아니라, 특정 컴포넌트만 따로 분리해서 로드할 수도 있음!
예를 들어, 무거운 컴포넌트를 동적으로 로드하면 초기 로딩 속도를 크게 줄일 수 있음.

1) React.lazy() 사용 (React 기본 제공 코드 스플리팅)

import React, { Suspense, lazy } from "react";

const HeavyComponent = lazy(() => import("./HeavyComponent"));

export default function App() {
  return (
    <div>
      <h1>Main Page</h1>
      <Suspense fallback={<p>Loading...</p>}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

📌 설명

  • React.lazy()를 사용하여 HeavyComponent필요할 때만 로드.
  • 초기 로딩 속도를 줄이고, HeavyComponent가 필요한 시점에만 불러옴.
  • Suspense를 사용해 로딩 중일 때 보여줄 UI(fallback)을 설정.

2) Next.js에서 dynamic import 사용

import dynamic from "next/dynamic";

const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
  ssr: false, // 서버사이드 렌더링 비활성화
  loading: () => <p>Loading...</p>,
});

export default function Home() {
  return (
    <div>
      <h1>Welcome</h1>
      <HeavyComponent />
    </div>
  );
}

📌 설명

  • dynamic()을 사용하여 HeavyComponent처음에는 로드하지 않음.
  • ssr: false를 설정하면 클라이언트에서만 동적으로 로드.
  • 사용자가 해당 컴포넌트를 필요로 할 때만 다운로드하여 렌더링.

✅ 3. 라이브러리 코드 스플리팅 (Webpack SplitChunks)

React 프로젝트에서 불필요한 라이브러리 로드를 방지하려면, Webpack의 splitChunks 기능을 활용할 수 있습니다.

📌 예제 (webpack 설정)

module.exports = {
  optimization: {
    splitChunks: {
      chunks: "all",
    },
  },
};

📌 설명

  • splitChunks 옵션을 활성화하면, React, Lodash 같은 공통 라이브러리를 따로 분리하여 캐싱할 수 있음.
  • 결과적으로 브라우저가 중복 요청을 줄이고 성능이 향상됨.

💡 코드 스플리팅이 성능에 주는 효과

초기 로딩 속도 개선 → 사용자 경험(UX) 향상
필요한 코드만 로드 → 불필요한 리소스 낭비 감소
코드 관리가 용이해짐 → 유지보수 쉬워짐


📌 결론

코드 스플리팅이란?
👉 "모든 JavaScript 코드를 한 번에 로드하는 것이 아니라, 필요한 순간에만 로드하는 최적화 기법"

어떻게 구현할까?
1️⃣ 페이지 단위 코드 스플리팅 (Next.js의 기본 기능)
2️⃣ 컴포넌트 단위 코드 스플리팅 (React.lazy(), dynamic import())
3️⃣ 라이브러리 코드 스플리팅 (webpack splitChunks 사용)


📌 정리하면?

💡 "코드 스플리팅은 필요할 때만 코드(파일)를 로딩하여, 웹 성능을 최적화하는 기법이다!"
💡 "React, Next.js, Webpack을 활용하여 코드 스플리팅을 적용할 수 있다."


next.js 다이나믹 임포트 설명을 보는데, 어디서 많이 본 듯한 얘기가 있어서

필요할 때마다 로드한다? -> 이거 useEffect 에서 dependency 넣어주면 그렇게 할 수 있는거 아니었나? -> 어? 근데 그건 state 잖아? -> 이건 그럼 파일 단위로(컴포넌트?) 로드를 관리할 수 있는 건가?

라고 의식의 흐름으로 생각했고 틀렸는지 GPT 한테 물어봤다.

GPT

네, 아주 좋은 관점을 가지고 계시네요! useEffect와 Dynamic Import의 차이점을 명확히 이해하면 더욱 깊이 있는 개발자가 될 수 있습니다.
비슷한 개념이 있지만 작동 방식과 목적이 다릅니다. 차이를 정리해볼게요.


📌 useEffect vs. Dynamic Import 차이점

개념설명
useEffect특정 state나 prop이 변경될 때 실행되는 사이드 이펙트 관리 (API 호출, 이벤트 리스너 등록 등)
Dynamic Import특정 컴포넌트나 파일을 필요할 때만 불러오는 코드 스플리팅 기법

✅ 1. useEffect는 State 기반으로 동작

useEffect컴포넌트가 렌더링되거나 특정 state/props가 변경될 때 실행됩니다.
이것은 state를 다루는 것이지, 코드를 로딩하는 것과는 다릅니다.

예제 🔽

import { useEffect, useState } from "react";

function MyComponent() {
  const [data, setData] = useState(null);

  // useEffect는 data가 없을 때 API를 호출하여 가져옴
  useEffect(() => {
    fetch("https://api.example.com/data")
      .then((res) => res.json())
      .then((result) => setData(result));
  }, []); // 빈 배열 → 처음 한 번 실행

  return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}

📌 설명

  • 처음 한 번만 실행([] 의존성 배열 사용).
  • API에서 데이터를 가져와서 data라는 state를 변경함.
  • 여기서 중요한 점은 코드를 가져오는 것이 아니라, 데이터를 가져오는 것!

✅ 2. Dynamic Import는 "코드"를 필요할 때만 로드

Dynamic Import는 컴포넌트나 특정 모듈을 아예 필요할 때까지 로드하지 않음.
즉, state가 아니라 파일(코드) 자체를 동적으로 불러오는 것!

예제 🔽

import dynamic from "next/dynamic";

// 'HeavyComponent'를 필요할 때만 가져옴
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
  ssr: false, // 서버에서 렌더링하지 않음 (클라이언트에서만 로드)
  loading: () => <p>Loading...</p>, // 로딩 UI 표시
});

export default function Home() {
  return (
    <div>
      <h1>Welcome</h1>
      <HeavyComponent />
    </div>
  );
}

📌 설명

  • HeavyComponent는 처음부터 로드되지 않음.
  • 이 컴포넌트가 실제로 필요할 때만 import!
  • 초기 번들 크기를 줄이고 성능을 최적화할 수 있음.

📌 핵심 차이점 요약

비교useEffectDynamic Import
목적특정 state 변경 시 실행할 작업(사이드 이펙트)특정 코드(컴포넌트/모듈)를 필요할 때만 로드
작동 방식상태(state)나 props의 변화 감지코드를 필요할 때만 가져옴
예제API 호출, 이벤트 리스너 등록 등코드 스플리팅(Code Splitting)
실제 활용fetch(), localStorage, eventListenerimport()를 사용하여 컴포넌트 동적 로딩
렌더링 시점렌더링 후 실행됨아예 처음부터 코드가 로딩되지 않음!

📌 결론

👉 useEffect는 "state 기반"으로 동작하며, 기존의 로드된 코드 내에서 특정 작업을 실행합니다.
👉 Dynamic Import는 "파일(코드) 기반"으로 동작하며, 아예 처음부터 로드되지 않고 필요할 때만 가져옴.

📌 쉽게 정리하면?

  • useEffect = "페이지가 렌더링되거나 상태가 바뀔 때 실행되는 로직 (API 호출, 이벤트 리스너 등)"
  • Dynamic Import = "아예 코드를 처음부터 로드하지 않고, 특정 시점에 로드하는 방식 (코드 스플리팅)"

0개의 댓글