Section 5. 데이터 페칭

OlMinJe·2025년 10월 15일

Next.js

목록 보기
14/20
post-thumbnail

인프런 "한 입 크기로 잘라먹는 Next.js" 수강

1️⃣ 앱 라우터의 데이터 페칭

기존 Page Router에서는 서버에서 데이터를 불러오기 위해 getServerSideProps, getStaticProps, getStaticPaths 와 같은 전용 함수를 사용했다.

page router의 데이터 페칭 과정 1
이 함수들이 데이터를 불러오고, 그 결과를 props 형태로 컴포넌트에 전달했다.

즉, 모든 데이터가 "페이지 단위"로만 전달될 수 있었음.

page router의 데이터 페칭 과정 2


⚠️ Page Router의 문제점

  • 모든 컴포넌트가 클라이언트 컴포넌트로 동작함.
  • 이로 인해 데이터 페칭이 서버와 브라우저에서 모두 실행되어 비효율적.
  • 또한, 데이터를 최상위 페이지 컴포넌트에서만 페칭할 수 있어, 하위 컴포넌트로 넘겨주러면 props 전달 혹은 Context API를 사용해야 했음

page router의 데이터 페칭 문제점


📢 App Router의 등장

서버 컴포넌트의 등장으로 문제 해결!

App Router에서는 모든 컴포넌트가 기본적으로 서버 컴포넌트로 동작한다. 즉, 브라우저에서 실행되지 않기 때문에 async와 같은 키워드를 붙여도 문제없다.

“데이터는 필요한 곳에서 직접 불러와라” - Next.js 공식문서 (Fetching data where it’s needed)

덕분에 각 컴포넌트가 스스로 필요한 데이터를 fetch해서 렌더링할 수 있게 되었다.

app router의 데이터 페칭


실습해보자!

import BookItem from '@/components/book-item';
import books from '@/mock/books.json';
import style from './page.module.css';

export default function Home() {
  return (
    <div className={style.container}>
      <section>
        <h3>지금 추천하는 도서</h3>
        {books.map((book) => (
          <BookItem key={book.id} {...book} />
        ))}
      </section>
      <section>
        <h3>등록된 모든 도서</h3>
        {books.map((book) => (
          <BookItem key={book.id} {...book} />
        ))}
      </section>
    </div>
  );
}

데이터 페칭을 할 수 있도록 async 키워드로 비동기 함수를 만든 후 아래와 같이 작성했다.

import BookItem from '@/components/book-item';
import { BookData } from '@/types';
import style from './page.module.css';

async function AllBooks() {
  const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/books`);
  if (!response.ok) {
    return <div>오류가 발생했습니다...</div>;
  }

  const allBooks: BookData[] = await response.json();

  return (
    <div>
      {allBooks.map((book) => (
        <BookItem key={book.id} {...book} />
      ))}
    </div>
  );
}

async function RecoBooks() {
  const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/books/random`);
  if (!response.ok) {
    return <div>오류가 발생했습니다...</div>;
  }

  const randomBooks: BookData[] = await response.json();

  return (
    <div>
      {randomBooks.map((book) => (
        <BookItem key={book.id} {...book} />
      ))}
    </div>
  );
}

export default function Home() {
  return (
    <div className={style.container}>
      <section>
        <h3>지금 추천하는 도서</h3>
        <RecoBooks />
      </section>
      <section>
        <h3>등록된 모든 도서</h3>
        <AllBooks />
      </section>
    </div>
  );
}

3️⃣ 데이터 캐시 (Data Cache)

Next.js는 fetch() 결과를 서버 측에서 자동으로 캐싱할 수 있도록 만들었다.
이 덕분에 동일한 요청이 반복될 때, 데이터를 어떤 방식으로 캐싱할지 지정할 수 있다.

  • 영구적으로 데이터를 보관
  • 혹은 특정 시간을 주기로 데이터 갱신

등의 기능을 수행하며, 사용 방법은 아래와 같다.

const response = await fetch(`/api`, { cache: "force-cache" });

Cache 옵션

옵션설명
cache: "no-store"캐싱하지 않음 (기본값). 항상 새로운 요청을 보냄
cache: "force-cache"요청 결과를 무조건 캐싱. 이후 동일 요청은 캐시에서 가져옴
next: { revalidate: 3 }3초마다 캐시를 갱신 (ISR 방식과 유사)
next: { tags: ['a'] }On-Demand Revalidation — 특정 태그를 지정해 수동으로 최신화

{ cache: "no-store" }

  • 데이터 페칭의 결과를 아예 캐싱하지 않도록 설정하는 옵션
  • 기본값으로 지정되는 옵션이다.

캐시 미저장 옵션 콘솔 결과

새로고침 후 콘솔에서 확인해보면cache skip으로 출력됨을 확인할 수 있다.


{ cache: "force-cache" }

  • 요청의 결과를 무조건 캐싱하며, 한 번 호출된 이후에는 다시 호출되지 않는다.

캐시 저장 옵션 콘솔 결과

새로고침 후. 콘솔로 확인해보면 cache hit 즉, 캐싱된 데이터를 찾는다는 것을 알 수 있다.


{ next: { revalidate: 3 } }

  • 특정 시간을 주기로 캐시를 업데이트 하며, 마치 Page Router의 ISR 방식과 유사하다.

실행 후, 지정한 시간을 주기로 새로고침 되는 것을 확인할 수 있다.


{ next: { tags: ['a'] } }

  • 요청이 들어왔을 때 데이터를 최신화한다.(On-Demand Revalidate)

추가로 데이터 페칭이 발생할 때마다 로그를 출력하는 설정은 아래와 같다.

import type { NextConfig } from 'next';
>
const nextConfig: NextConfig = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
};
>
export default nextConfig;

3. Request Memoization

“요청을 기억한다.” — 동일한 요청을 자동으로 한 번만 수행하도록 최적화하는 기능이다.

서버 컴포넌트 구조상, 여러 컴포넌트가 같은 데이터를 요청할 수 있다. (ex. 각 서버 컴포넌트들이 자신들이 필요한 데이터를 알아서 불러오기 때문!)
이때 Next.js는 동일한 요청을 하나로 묶어 처리하고, 결과를 공유한다.

Request Memoization 동작 과정

데이터 캐시 vs 리퀘스트 메모이제이션

구분데이터 캐시 (Data Cache)요청 메모이제이션 (Request Memoization)
목적데이터를 오래 보관동일한 요청을 중복 방지
지속 기간서버 인스턴스가 유지되는 동안렌더링 1회 주기 동안만
활용 예API 결과 영구 저장한 페이지 내 중복 요청 제거
profile
큐트걸

0개의 댓글