Next.js 데이터 가져오기
규칙
- 서버 컴포넌트에서
async/await로 데이터를 가져오세요. 초기 데이터에 useEffect를 사용하지 마세요.
fetch()는 { cache: 'no-store' } 또는 next: { revalidate }를 지정하지 않는 한 App Router에서 기본적으로 캐시됩니다. 원하는 동작을 명확히 파악하세요.
- 독립적인 요청은
Promise.all로 병렬 처리하고, 의존성이 있는 경우에만 순차 처리하세요.
- 느린 섹션은
<Suspense fallback> + loading.tsx로 감싸 스트리밍하세요.
- 온디맨드 무효화를 위해
next: { tags: ['posts'] } + revalidateTag('posts')를 사용하세요.
- 클라이언트 사이드 페칭(SWR/React Query)은 인터랙티브 UI/필터 UI에만 사용하고, 초기 렌더링에는 사용하지 마세요.
패턴
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
});
if (!res.ok) throw new Error('게시물을 가져오지 못했습니다');
return res.json();
}
async function getInbox(userId: string) {
const res = await fetch(`https://api.example.com/inbox/${userId}`, {
cache: 'no-store',
});
return res.json();
}
export default async function Dashboard() {
const [user, posts] = await Promise.all([getUser(), getPosts()]);
return <View user={user} posts={posts} />;
}
import { Suspense } from 'react';
export default function Page() {
return (
<div>
<FastHeader />
<Suspense fallback={<CommentsSkeleton />}>
<SlowComments />
</Suspense>
</div>
);
}
import { revalidateTag } from 'next/cache';
export async function POST(req: Request) {
const { tag, secret } = await req.json();
if (secret !== process.env.REVALIDATE_SECRET)
return Response.json({ ok: false }, { status: 401 });
revalidateTag(tag);
return Response.json({ ok: true });
}
체크리스트