다이나믹 로딩으로 코드 스플리팅을 해서 성능을 향상시킨다는데 무슨 말인지 모르겠음!!!
그래서 다이나믹 로딩이 뭔지 부터 물어봤다...
다이나믹 로딩(Dynamic Import)이란 필요한 순간에만 특정 코드나 컴포넌트를 로드하는 기술입니다.
Next.js에서는 import()와 next/dynamic을 사용하여 동적 로딩을 구현할 수 있습니다.
💡 왜 Dynamic Import를 사용할까요?
- 초기 로딩 속도 개선: 처음 페이지를 불러올 때 불필요한 코드까지 함께 로드하면 속도가 느려짐.
- 번들 크기 감소: 코드 스플리팅(Code Splitting)을 통해 필요한 코드만 불러옴.
- 서버 사이드 렌더링(SSR) 비활성화 가능: 클라이언트에서만 실행해야 하는 코드(예: 브라우저 전용 라이브러리)를 다룰 수 있음.
Next.js에서는 next/dynamic을 사용해 다이나믹 로딩을 쉽게 구현할 수 있습니다.
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는 로딩되지 않으며, 실제로 필요한 순간에만 불러옴.일부 컴포넌트는 클라이언트에서만 실행되어야 합니다.
예를 들어, 브라우저 전용 라이브러리(로컬 스토리지, 창 크기 감지 등)를 사용하는 경우 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 객체 등)를 사용할 때 유용합니다.다이나믹 로딩 시 로딩 화면(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..." 문구가 표시됨.다양한 컴포넌트를 각각 필요할 때만 로드하도록 설정할 수도 있습니다.
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>
);
}
📌 설명
HeavyChart는 SSR을 비활성화하고, 클라이언트에서만 로드되도록 설정.HeavyMap은 로딩 상태를 추가하여 지도 로드 시간이 길어도 사용자에게 피드백 제공.loading 옵션으로 해결 가능.| 기능 | 코드 예제 | 설명 |
|---|---|---|
| 기본 Dynamic Import | dynamic(() => import("../components/HeavyComponent")) | 필요할 때만 로드 |
| SSR 비활성화 | { ssr: false } | 브라우저 전용 코드에 사용 |
| 로딩 상태 표시 | { loading: () => <p>Loading...</p> } | 로딩 화면 추가 |
| 여러 개의 Dynamic Import | dynamic(() => import("../components/HeavyChart")) | 각 컴포넌트를 동적 로딩 |
✅ 적용하면 좋은 경우
window, localStorage 사용) 🚫 사용을 피해야 할 경우
코드 스플리팅(Code Splitting)은 웹 애플리케이션의 JavaScript 번들을 작은 조각들로 나누어, 필요한 순간에만 로드하는 최적화 기법입니다.
즉, 한 번에 모든 코드를 불러오는 것이 아니라, 실제로 필요한 코드만 로딩하여 성능을 개선하는 방법입니다.
웹사이트를 처음 로드할 때 모든 JavaScript 파일을 한 번에 다운로드하면, 다음과 같은 문제가 발생합니다.
❌ 문제점
1. 초기 로딩 속도가 느려짐 → 사용자 경험(UX)이 나빠짐
2. 사용하지 않는 코드도 함께 로드됨 → 불필요한 리소스 낭비
3. 페이지가 무거워짐 → 성능 저하
📌 해결 방법: 코드 스플리팅!
코드 스플리팅은 여러 방법으로 구현할 수 있습니다.
| 방법 | 설명 | 예제 |
|---|---|---|
| 1. 페이지 단위 코드 스플리팅 | 페이지마다 코드를 분리하여, 특정 페이지에 들어갈 때만 로드 | Next.js의 자동 코드 스플리팅 |
| 2. 컴포넌트 단위 코드 스플리팅 | 무거운 컴포넌트를 동적으로 가져와서 필요한 순간에 로드 | React.lazy(), dynamic import() |
| 3. 라이브러리 코드 스플리팅 | 사용하지 않는 라이브러리를 한꺼번에 로드하지 않고, 필요한 시점에 로드 | webpack의 splitChunks 옵션 |
Next.js는 기본적으로 각각의 페이지를 별도의 JavaScript 파일로 분리하여 자동으로 코드 스플리팅을 수행합니다.
/pages
├── index.js → 첫 화면을 위한 코드만 로드됨
├── about.js → "About" 페이지에 들어갈 때만 로드됨
├── contact.js → "Contact" 페이지에 들어갈 때만 로드됨
📌 설명
index.js 페이지에 있을 때는 about.js, contact.js 코드를 로드하지 않음.about 페이지로 이동하면, 그때서야 about.js 파일을 로드함 → 초기 로딩 속도 개선!📌 즉, Next.js를 사용하면 페이지별로 자동으로 코드가 분리됨!
페이지가 아니라, 특정 컴포넌트만 따로 분리해서 로드할 수도 있음!
예를 들어, 무거운 컴포넌트를 동적으로 로드하면 초기 로딩 속도를 크게 줄일 수 있음.
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)을 설정.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를 설정하면 클라이언트에서만 동적으로 로드.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 한테 물어봤다.
네, 아주 좋은 관점을 가지고 계시네요! useEffect와 Dynamic Import의 차이점을 명확히 이해하면 더욱 깊이 있는 개발자가 될 수 있습니다.
비슷한 개념이 있지만 작동 방식과 목적이 다릅니다. 차이를 정리해볼게요.
| 개념 | 설명 |
|---|---|
| useEffect | 특정 state나 prop이 변경될 때 실행되는 사이드 이펙트 관리 (API 호출, 이벤트 리스너 등록 등) |
| Dynamic Import | 특정 컴포넌트나 파일을 필요할 때만 불러오는 코드 스플리팅 기법 |
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>;
}
📌 설명
[] 의존성 배열 사용). data라는 state를 변경함. 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는 처음부터 로드되지 않음. | 비교 | useEffect | Dynamic Import |
|---|---|---|
| 목적 | 특정 state 변경 시 실행할 작업(사이드 이펙트) | 특정 코드(컴포넌트/모듈)를 필요할 때만 로드 |
| 작동 방식 | 상태(state)나 props의 변화 감지 | 코드를 필요할 때만 가져옴 |
| 예제 | API 호출, 이벤트 리스너 등록 등 | 코드 스플리팅(Code Splitting) |
| 실제 활용 | fetch(), localStorage, eventListener | import()를 사용하여 컴포넌트 동적 로딩 |
| 렌더링 시점 | 렌더링 후 실행됨 | 아예 처음부터 코드가 로딩되지 않음! |
👉 useEffect는 "state 기반"으로 동작하며, 기존의 로드된 코드 내에서 특정 작업을 실행합니다.
👉 Dynamic Import는 "파일(코드) 기반"으로 동작하며, 아예 처음부터 로드되지 않고 필요할 때만 가져옴.
📌 쉽게 정리하면?
useEffect= "페이지가 렌더링되거나 상태가 바뀔 때 실행되는 로직 (API 호출, 이벤트 리스너 등)"Dynamic Import= "아예 코드를 처음부터 로드하지 않고, 특정 시점에 로드하는 방식 (코드 스플리팅)"