Next.jsNext.js 기초강의 8개 중 3번째난이도 기초

데이터 패칭 — 서버 컴포넌트와 캐싱 전략

발행 읽는 시간 약 1 분

  • Next.js
  • 데이터패칭
  • fetch
  • 캐싱
  • Suspense
  • 서버컴포넌트

강의 소개

Next.js에서 서버 컴포넌트로 데이터를 가져오는 방법을 배웁니다. fetch 캐싱, revalidate, Suspense를 활용한 스트리밍 렌더링도 익힙니다.

서버 컴포넌트에서 데이터 패칭#

TSX
// app/products/page.tsx
// async 함수로 직접 데이터 패칭 가능
async function ProductsPage() {
    // fetch는 서버에서 실행됨
    const products = await fetch("https://api.example.com/products")
        .then(r => r.json());

    return (
        <ul>
            {products.map((p: Product) => (
                <li key={p.id}>{p.name}</li>
            ))}
        </ul>
    );
}

fetch 캐싱 옵션#

TSX
// 기본: 빌드 시 한 번 캐시 (SSG와 유사)
fetch("https://api.example.com/data");

// 캐시 없음: 매 요청마다 새로 가져옴 (SSR)
fetch("https://api.example.com/data", {
    cache: "no-store",
});

// 주기적 재검증 (ISR)
fetch("https://api.example.com/data", {
    next: { revalidate: 3600 },  // 1시간마다 재검증
});

// 태그 기반 재검증
fetch("https://api.example.com/products", {
    next: { tags: ["products"] },
});

태그 기반 revalidation#

TSX
// app/actions/revalidate.ts
import { revalidateTag } from "next/cache";

// 특정 태그의 캐시를 즉시 무효화
export async function revalidateProducts() {
    revalidateTag("products");
}

// API Route나 Server Action에서 호출

병렬 데이터 패칭#

TSX
async function DashboardPage() {
    // 순차적 (느림)
    const user = await fetchUser();
    const orders = await fetchOrders(user.id);

    // 병렬 (빠름)
    const [user2, stats, notifications] = await Promise.all([
        fetchUser(),
        fetchStats(),
        fetchNotifications(),
    ]);

    return (
        <div>
            <UserCard user={user2} />
            <StatsGrid stats={stats} />
        </div>
    );
}

Suspense로 스트리밍 렌더링#

TSX
import { Suspense } from "react";

// 느린 컴포넌트를 Suspense로 감싸면
// 나머지 페이지를 먼저 보여줌
async function SlowComponent() {
    await new Promise(r => setTimeout(r, 3000));  // 3초 대기
    const data = await fetchSlowData();
    return <div>{data}</div>;
}

export default function Page() {
    return (
        <div>
            <h1>즉시 표시</h1>
            <Suspense fallback={<div>데이터 로딩 중...</div>}>
                <SlowComponent />
            </Suspense>
            <p>이것도 즉시 표시</p>
        </div>
    );
}

클라이언트에서 데이터 패칭#

인증이 필요하거나 실시간 업데이트가 필요한 경우:

TSX
"use client";
import { useState, useEffect } from "react";

function ClientDataComponent({ userId }: { userId: string }) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        fetch(`/api/users/${userId}/private-data`)
            .then(r => r.json())
            .then(d => { setData(d); setLoading(false); });
    }, [userId]);

    if (loading) return <div>로딩 중...</div>;
    return <div>{JSON.stringify(data)}</div>;
}

generateMetadata: 동적 메타데이터#

TSX
// app/products/[id]/page.tsx
import type { Metadata } from "next";

interface Props {
    params: { id: string };
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
    const product = await fetch(`https://api.example.com/products/${params.id}`)
        .then(r => r.json());

    return {
        title: product.name,
        description: product.description,
        openGraph: {
            images: [product.imageUrl],
        },
    };
}

export default async function ProductPage({ params }: Props) {
    const product = await fetch(`https://api.example.com/products/${params.id}`)
        .then(r => r.json());

    return <div>{product.name}</div>;
}

정리#

Diagram · Mermaid
옵션동작사용 사례
기본 (캐시)빌드 시 생성블로그, 문서
revalidate: NN초마다 갱신제품 목록
no-store매 요청사용자 데이터
tags이벤트 기반 갱신CMS 연동

다음 편에서는 Server Actions와 API Routes — 서버 사이드 로직을 처리하는 방법을 배웁니다.

Follow · Ask · Work

이 글이 도움이 됐다면

짧은 작업 기록과 데모는 인스타그램에 올립니다. 만들고 싶은 것이 있다면 프로젝트 의뢰나 협업 제안도 편하게 보내 주세요.

글쓴이

파란여우

직접 만든 AI 앱·제품의 개발·배포를 공개하는 실전형 테크 크리에이터

파란여우 소개 Instagram @bluefox._.hi

이어서 보기

만들고 싶은 것이 있다면 프로젝트 의뢰로 알려 주세요.