Next.js(v15) 에서 데이터 캐시하기

sujin·2025년 2월 14일
1
post-thumbnail

고백하자면 저는 next.js13 버전만 사용해봤습니다.😅 14버전이 나왔을 때도 공부해야지 공부해야지 미루기만 하고 최근에서야 앱라우터 와 서버 컴포넌트를 사용하는 중입니다...ㅎㅎ

막상 사용해보니 이렇게 효율적일 수가 없더라구요 진작에 공부해서 써볼껄 이라는 후회가 들었습니다. next.js 15버전을 공부하던 중에 가장 인상 깊었던 것은 캐시 기능이었고 새로워진 next.js는 같은 일 계속 시키는거 정말 싫어하는 구나라고 느꼈습니다.

그래서 오늘은 next.js의 데이터 캐시하는 방법에 대해서 정리해보려고 합니다~!


1️⃣ 데이터 페칭

먼저, 데이터 페칭에 대해서 어떻게 이뤄지는지 간략히 알아보도록 하겠습니다.

Page Router 버전에서 데이터 페칭

  • 페이지 파일에서 SSR, SSG, Dynamic SSG를 통해 서버에서 데이터를 가져와서 page에 props로 데이터를 전달합니다.
  • 모든 컴포넌트가 클라이언트 컴포넌트로 동작합니다. (= 컴포넌트가 서버와 클라이언트에서 모두 실행된다는 것을 의미합니다.)
  • 최상단에서 데이터를 필요로 하는 페이지까지 props로 계속 전달해줘합니다. (props drilling 문제)

App Router 버전에서 데이터 페칭

  • 서버 컴포넌트에서 async - await 키워드와 fetch 메서드를 활용해서 데이터를 직접 불러오는 데이터 페칭 로직을 만들 수 있습니다.
     export async function Page(props){
    	 const data = await fetch('...');
    	 
    	 return <div>...</div>
	}
  • 하지만 클라이언트 컴포넌트에서는 async - await 키워드 를 사용할 수 없습니다.
  • 데이터가 필요한 곳에서 직접 불러오는 것을 권장합니다.

🧐 async - await 키워드 사용이 서버 컴포넌트에서는 되지만, 클라이언트 컴포넌트에서는 안되는 이유

  • 서버 컴포넌트는 서버에서 데이터를 미리 가져와서 HTML을 렌더링할 수 있기 때문입니다. 서버 측에서는 비동기 작업이 완료된 후 렌더링을 처리하므로, 비동기 작업을 문제없이 사용할 수 있습니다.
  • 반면, 클라이언트 컴포넌트는 브라우저에서 실행되며, 랜더링 시에 비동기 작업을 바로 처리할 수 없기 때문에 제대로 렌더링이 이루어지지 않을 수 있습니다.

☝🏻 주의사항
데이터 패칭을 위한 환경 변수를 설정할 때 .env 파일에서 NEXT_PUBLIC 이라는 접두사를 붙이지 않으면 next 는 자동으로 해당 환경변수를 서버측에서만 접근 가능한 것으로 인식하여 해당 환경변수에 대해 클라언트 컴포넌트에서는 접근이 불가능합니다!


2️⃣ 데이터 캐시

Next.js에서 데이터 캐시란

  • fetch 메서드를 활용해 불러운 데이터를 Next 서버에서 보관하는 기능을 말합니다.
  • 영구적으로 데이터를 보관하거나, 특정 시간을 주기로 갱신 시키는 것도 가능합니다.
  • 캐시의 목적은 불필요한 데이터 요청의 수를 줄여서 웹 서비스의 성능을 개선하는 것이라고 할 수 있습니다.

캐시 옵션

📢 Next 서버에서 발생하는 데이터 패칭을 로그로 남기는 설정

  import type { NextConfig } from "next";
  const nextConfig: NextConfig = {
    	logging: {
    		fetches: {
    			fullUrl: true,	
    		},
    	},
    };
  export default nextConfig;
  • next.config.ts 파일을 위와 같으 설정하면 터미널에서 데이터 패칭에 대한 로그를 확인할 수 있습니다!
const response = await fetch('~/api', {cache: "force-cache});
  • {cache: "force-cache"}
    • 요청의 결과를 무조건 캐싱합니다.
    • 한번 호출 된 이후에는 다시는 호출되지 않습니다.
  • {cache: "no-store"}
    • 기본값으로, 데이터 페칭의 결과를 저장하지 않습니다.
    • 캐싱을 아예 하지 않아 매번 데이터를 요청합니다.
  • {next: {revalidate: 3}}
    • 특정 시간을 주기로 캐시를 업데이트합니다.
    • 마치 Page Router의 ISR방식과 비슷하다고 생각하면 됩니다.
  • {next: {tags: ['a']}}
    • on-Demand Revalidate
    • 요청이 들어왔을 때 데이터를 최신화하는 방식입니다.

예시

책 리스트 불러오는 API로 예시를 살펴보도록 하겠습니다!

async function AllBooks() {
  const response = await fetch(
    `${process.env.NEXT_PUBLIC_API_SERVER_URL}/book`,
    { cache: "no-store" } // 이 부분을 원하는 캐시 옵션으로 설정합니다!
  );
  if (!response.ok) {
    return <div>오류가 발생했습니다...</div>;
  }
  const allBooks: BookData[] = await response.json();

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

export default function Home() {
  return (
    <div>
      <section>
        <h3>등록된 모든 도서</h3>
        <AllBooks />
      </section>
    </div>
  );
}
  • {cache: "force-cache"} 로 설정했을 때

  • {cache: "no-store"} 로 설정했을 때


3️⃣ 리퀘스트 메모이제이션

마지막으로 리퀘스트 메모이제이션이란 "요청을 기억한다." 라는 뜻으로, 중복된 요청이 발생하지 않도록 캐싱하는 것을 말합니다. 그렇다면 여기서 "데이터 캐시랑 뭐가 다른거지?" 라는 의문을 생길 수 있습니다.

✋🏻 데이터 캐시 VS 리퀘스트 메모이제이션

  • 데이터 케시백엔드 서버로부터 불러온 데이터를 거의 영구적으로 보관하기 위해 사용됩니다. 따라서 서버 가동중에는 영구적으로 보관됩니다.

  • 리퀘스트 메모이제이션하나의 페이지를 렌더링 하는 동안에 중복된 API 요청을 캐싱하기 위해 존재합니다. 따라서, 랜더링이 종료되면 모든 캐시가 소멸됩니다.

결론적으로 데이터 캐시는 지속적으로 유지되는 반면, 리퀘스트 메모이제이션은 렌더링 동안에만 유지된다고 정리할 수 있습니다.

🤷🏻‍♀️ 리퀘스트 메모이제이션의 등장 배경

리퀘스트 메모이제이션이 필요한 이유는 서버 컴포넌트의 도입 때문인데요 하나의 페이지에서 컴포넌트가 각각 자신이 필요한 데이터를 페칭하기 때문에 한 페이지에서 서로 다른 컴포넌트에서 동일한 API를 요청하는 경우가 발생할 수 있기 때문입니다.

그리고 리퀘스트 메모이제이션은 Next.js에서 자동으로 제공하는 기능으로 별도의 설정은 필요하지 않습니다!


📚 참고
한 입 크기로 잘라먹는 next.js

profile
개발댕발

0개의 댓글

관련 채용 정보