

Next.js를 사용할 때, 퍼포먼스와 사용자 경험을 동시에 높이는 대표적인 방법 중 하나가 코드 스플리팅과 레이지 로딩(Lazy Loading)입니다.
특히 next/dynamic은 “React.lazy()와 Suspense의 조합”처럼 보이지만, 실제로는 데이터 페칭에서 사용하는 Suspense와는 조금 다른 작동 방식을 가집니다.
이번 포스트에서는 next/dynamic를 제대로 이해하고, React Suspense와 함께 사용해 이중 로딩 지연을 최소화하는 방법을 살펴보겠습니다.
ssr: false 설정을 통해 완전히 클라이언트에서만 렌더링하도록 강제할 수 있습니다.import dynamic from 'next/dynamic';
const LazyLoadedComponent = dynamic(() => import('./MyComponent'), {
ssr: false,
loading: () => <p>Loading component...</p>,
});
위 설정으로 서버 사이드 렌더링을 하지 않음(ssr: false)을 명시하면, Next.js는 미니멀한 HTML과 Loading component... 메시지만 보내고, 브라우저에서 JS 파일이 로드된 후에야 실제 UI를 렌더링합니다.
Next.js 문서에는 next/dynamic가 “React.lazy()와 Suspense의 합성”이라고 표현되어 있지만, 아래 차이점을 유의해야 합니다:
즉, 컴포넌트를 다운받는 단계(Next.js의 loading)와, 컴포넌트가 데이터를 불러올 때 쓰는 Suspense 로딩은 별도로 동작합니다. 둘을 잘 조합하지 않으면, 서로 다른 로딩 UI가 순식간에 번갈아 나타날 수 있습니다.
React Suspense는 비동기 작업(데이터 페칭, 코드 스플리팅 등)이 완료될 때까지 UI 렌더링을 잠시 ‘유보’하는 메커니즘입니다. 이 로직을 이용해, 네트워크 요청이나 비동기 계산이 진행되는 동안 미리 정해둔 Fallback UI를 보여줄 수 있습니다.
다음 예시를 보겠습니다:
'use client';
import { Suspense } from 'react';
import dynamic from 'next/dynamic';
import { useClientStore } from '@/providers/client-store-provider';
import PantryBoxesSkeleton from './PantryBoxes/PantryBoxesSkeleton';
const GuestUserPantry = dynamic(() => import('./GuestUserPantry'), {
ssr: false,
loading: PantryBoxesSkeleton,
});
const LogInUserPantry = dynamic(() => import('./LogInUserPantry'), {
ssr: false,
loading: PantryBoxesSkeleton,
});
export default function Pantry() {
// 예: Zustand로 로그인 상태 확인
const isLoggedIn = useClientStore((state) => state.user.isLoggedIn);
const Component = isLoggedIn ? LogInUserPantry : GuestUserPantry;
return (
<Suspense fallback={<PantryBoxesSkeleton />}>
<Component />
</Suspense>
);
}
PantryBoxesSkeleton을 노출합니다.GuestUserPantry나 LogInUserPantry 안에서 React Query의 useSuspenseQuery 등으로 Suspense가 발생하면, <Suspense fallback={<PantryBoxesSkeleton />}>가 작동하여 다시 같은 스켈레톤을 보여주게 됩니다.이런 식으로 일관된 로딩 화면을 써서 이중 로딩 UI가 따로따로 뜨는 혼선을 줄일 수 있습니다.
Next.js의 next/dynamic는 “React.lazy()+Suspense”처럼 보이지만, 실제로는 컴포넌트 import에 대한 로딩만 책임질 뿐, 데이터 페칭을 위한 Suspense는 별도로 동작합니다. 따라서:
이 두 가지를 잘 결합하면, 부드러운 로딩 경험과 최적화된 성능을 동시에 얻을 수 있습니다.
---
Next.js 애플리케이션에서 이중 로딩이나 UI 깜빡임이 우려된다면, 꼭 ssr: false와 Suspense를 함께 고려해 보세요!