Next.js 에서 웹 페이지가 데이터를 가져오거나 처리하는 동안 사용자에게 대기 상태를 시각적으로 보여주기 위해 로딩 UI 를 생성하는 두 가지 방법에 대해 알아보자.
<Suspense>
사용하기Suspense
모듈을 가져와, 반환하려는 컴포넌트를 Suspense
태그로 감싸준다.Suspense
태그에 fallback
속성을 적용해, 로딩 시 렌더링 될 UI를 작성한다.app/users/page.tsx
import React from "react";
import Link from "next/link";
import UserTable from "./UserTable";
import { Suspense } from "react";
interface Props {
searchParams: { sortOrder: string };
}
const UsersPage = ({ searchParams: { sortOrder } }: Props) => {
return (
<div>
<div>This is UserPage</div>
<Link href="/users/new" className="btn btn-primary">
New user
</Link>
<p>{new Date().toLocaleTimeString()}</p>
<Suspense fallback={<p>Loading...</p>}>
<UserTable sortOrder={sortOrder} />
</Suspense>
</div>
);
};
export default UsersPage;
Suspended : false
는 데이터가 잘 도착했으며, 최종 페이지가 렌더링 된 상태임을 의미한다.Suspensed : true
로 변경할 수 있으며, 변경하면 작성했던 로딩 UI 가 출력된다.Suspended : false
로 바꾸고, 개발자 도구 - Network 탭으로 이동해 페이지 새로고침을 누르면, 서버에서 받은 문서를 확인할 수 있다.특정 페이지에 여러 개의 서스펜스 컴포넌트를 가지고 있거나, 모든 페이지에 공통의 서스펜스 컴포넌트를 가지고 싶은 경우에는 어떻게 해야할까?
Suspense
컴포넌트로 감싸준다.Suspense
컴포넌트로 감싸고 원하는 로딩 UI 를 fallback
속성으로 전달해주면 된다.app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import NavBar from "./NavBar";
import { Suspense } from "react";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" data-theme="winter">
<body className={inter.className}>
<NavBar />
<main className="p-5">
<Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
</main>
</body>
</html>
);
}
loading.tsx
파일을 사용하는 것!loading.tsx
사용하기loading.tsx
page.tsx
, layout.tsx
와 같이 Next.js에서 약속한 파일명loading.tsx
파일을 생성하고, 컴포넌트를 렌더링하면 된다.loading.tsx
파일을 생성하면, 해당 라우터에 대한 로딩 UI를 출력할 수 있다.app/users/loading.tsx
app/admin/loading.tsx
app/products/loading.tsx
app/loading.tsx
import React from "react";
const Loading = () => {
return <div>Loading..!</div>;
};
export default Loading;