dev.syw

서버 컴포넌트에서 fetch 하고 캐시를 제어한다.

데이터 패칭과 캐싱

App Router에서 데이터 패칭은 놀랄 만큼 단순합니다. 서버 컴포넌트를 async 로 만들고 그냥 await fetch(...) 하면 됩니다. 대신 Next.js가 fetch 를 확장해 강력한 캐싱을 더했기 때문에, 그 동작을 이해하는 게 핵심입니다.

학습 목표

  • 서버 컴포넌트에서 async/await 로 데이터를 읽는다.
  • fetch 의 캐시 옵션(force-cache, no-store, revalidate)을 구분한다.
  • 병렬 패칭과 순차 패칭의 차이와 폭포(waterfall)를 피하는 법을 안다.
  • 같은 요청이 자동으로 합쳐지는 request memoization을 이해한다.

서버 컴포넌트에서 패칭

별도 라이브러리 없이 컴포넌트 안에서 바로 데이터를 가져옵니다.

// app/products/page.tsx
export default async function Products() {
  const res = await fetch('https://api.example.com/products');
  const products = await res.json();

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

서버에서 실행되므로 API 키를 헤더에 붙여도 브라우저에 노출되지 않습니다.

fetch 캐시 옵션

Next.js의 fetch 는 결과를 캐시할 수 있습니다. cachenext.revalidate 옵션으로 제어합니다.

// 1) 영구 캐시 (정적): 빌드 결과를 재사용
await fetch(url, { cache: 'force-cache' });

// 2) 매번 새로: 캐시하지 않음 (항상 최신)
await fetch(url, { cache: 'no-store' });

// 3) 시간 기반 재검증: 60초마다 새로 가져옴 (ISR)
await fetch(url, { next: { revalidate: 60 } });
옵션동작쓰임
force-cache한 번 받고 계속 재사용잘 안 바뀌는 데이터
no-store매 요청마다 새로실시간·개인화 데이터
next: { revalidate: N }N초마다 갱신적당히 자주 바뀌는 데이터

💡 TIP — 라우트 전체의 동작을 한 번에 바꾸려면 페이지 파일에 export const dynamic = 'force-dynamic' 또는 export const revalidate = 60 을 둘 수도 있습니다. (다음 레슨에서 자세히)

병렬 vs 순차 패칭

여러 데이터를 가져올 때, 무심코 await 를 줄줄이 쓰면 폭포(waterfall) 가 생깁니다. 앞 요청이 끝나야 다음이 시작되어 느려집니다.

// ❌ 순차: user 끝나야 posts 시작 (느림)
const user = await getUser();
const posts = await getPosts();

서로 의존하지 않는 요청은 Promise.all동시에 보냅니다.

// ✅ 병렬: 둘을 동시에 요청
const [user, posts] = await Promise.all([getUser(), getPosts()]);

⚠️ 주의 — 다음 요청이 앞 요청의 결과를 필요로 할 때만 순차로 두세요. 그렇지 않다면 거의 항상 병렬이 빠릅니다.

Request Memoization

같은 렌더링 패스 안에서 완전히 동일한 fetch 요청(같은 URL, 같은 옵션)을 여러 번 호출하면, Next.js가 자동으로 한 번만 실제 요청하고 결과를 공유합니다. 그래서 데이터를 props로 줄줄이 내려보내지 않고, 필요한 컴포넌트에서 각자 fetch 해도 됩니다.

// 같은 요청이 두 번 호출되어도 실제 네트워크 요청은 1회
async function getUser() {
  const res = await fetch('https://api.example.com/me'); // 자동 합쳐짐
  return res.json();
}

// Layout 에서 한 번, Page 에서 한 번 호출해도 OK

이 덕분에 "데이터를 위에서 한 번 받아 아래로 전달" 하는 prop drilling을 줄일 수 있습니다.

요약

  • 서버 컴포넌트를 async 로 만들고 await fetch 로 데이터를 읽는다.
  • force-cache/no-store/revalidate 로 캐시 동작을 고른다.
  • 의존성이 없는 요청은 Promise.all 로 병렬화해 폭포를 피한다.
  • 동일한 fetch 는 자동으로 합쳐진다(request memoization).

연습문제

  1. 공개 API를 골라 no-storerevalidate: 30 으로 각각 패칭하고, 새로고침 시 데이터가 어떻게 다른지 관찰하세요.
  2. 서로 독립적인 두 API 호출을 Promise.all 로 병렬화해 보세요.
  3. 같은 fetch 를 두 컴포넌트에서 호출해도 네트워크 요청이 한 번만 일어나는지 개발자 도구로 확인하세요.
  4. 어떤 데이터에 force-cache 가 적합하고, 어떤 데이터에 no-store 가 적합한지 예를 두 개씩 적어 보세요.

힌트 — 순차 await 두 줄을 Promise.all([a(), b()]) 로 바꾸면 됩니다. 캐시 동작이 헷갈리면 네트워크 탭과 응답 시간을 비교해 보세요.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

Next.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.