본문 바로가기
Resource/웹 프론트엔드

[Next.js] 데이터 패칭(Fetching), 캐싱(Caching), 재검증(Revalidating)

by 우창욱 2024. 2. 20.

 

next.js의 공식 문서를 한글로 번역하면서 공부한 내용을 정리한 글입니다.

 

Data Fetching: Fetching, Caching, and Revalidating | Next.js

Learn how to fetch, cache, and revalidate data in your Next.js application.

nextjs.org

 

서버에서 fetch를 이용한 데이터 패칭 / 캐싱 / 재검증

Next.js에서는 fetch Web API를 확장해서, 사용자가 서버의 각 요청에 대한 캐싱, 재검증 동작을 구성할 수 있도록 합니다. 그리고 React는 컴포넌트 트리를 렌더링하는 동안 `fetch` 를 자동적으로 메모(memoize)합니다.

Next.js에서는 서버 컴포넌트(server component), 라우트 핸들러(route handlers), 그리고 서버 액션(server action)에서 fetchasync / await과 함께 사용할 수 있습니다.

 

데이터 캐싱 (Caching Data)

캐싱은 데이터를 저장하므로, 새로운 요청이 올 때 마다 캐싱된 데이터를 제공할 수 있습니다. 기본적으로 Next.js는 `fetch` 의 반환 값을 서버의 데이터 캐시(Data Cache)에 자동으로 캐시합니다. 이는 빌드 시에 그리고 각각의 데이터 요청 시에, 패칭되고 캐시되고, 재사용될 수 있다는 것을 의미합니다.

// 'force-cache'는 기본값입니다. 생략 가능.
fetch('https://...', { cache: 'force-cache' })

 

POST 메서드를 사용하는 `fetch` 요청도 또한 자동적으로 캐싱됩니다. 그러나 라우트 핸들러의 POST 메서드는 캐시되지 않습니다.

 

데이터 캐시란?

데이터 캐시는 영구 HTTP 캐시 입니다. 플랫폼에 따라 캐시는 자동으로 확장되고 여러 지역으로 공유될 수 있습니다.

 

데이터 재검증 (Revalidating Data)

재검증은 데이터 캐시를 제거하고, 최신 데이터를 다시 가져오는 프로세스입니다. 이는 데이터가 변경되어서 최신 정보를 표현하려는 경우에 유용합니다. 

 

캐시된 데이터는 아래 두 가지 방법으로 유효성을 검사할 수 있습니다.

이름 내용
1. 시간 기반 재검증 (Time-based revalidation) 일정 시간이 지나면 데이터를 자동으로 재검증합니다.
자주 변경되지 않고 최신성이 중요하지 않은 데이터에 유용합니다.
2. 주문형 재검증 (On-demand revalidation) 이벤트 기반으로(ex. 폼 제출) 데이터를 수동으로 재검증합니다.
주문형 재검증에서는 태그 기반 또는 경로 기반 접근 방식을 사용하여 데이터 그룹을 한 번에 재검증 할 수 있습니다.
가능한 한 빨리 최신 데이터를 표현하는 경우에 유용합니다.
 

시간 기반 재검증 (예시)

fetch('https://...', { next: { revalidate: 3600 } });

 

또는, 경로 세그먼트의 모든 `fetch` 요청을 재검증 하려면, 세그먼트 구성 옵션 (segment config options)을 사용할 수 있습니다.

// layout.js | page.js
export const revalidate = 3600 // 1시간 마다 재검증

 

정적(static, 이미지)으로 렌더링 된 경로에 여러 개의 `fetch` 요청이 있고, 각각의 재검증 빈도가 다른 경우, 가장 적은 시간에 맞춰서 재검증 됩니다. 동적(dynamic, 이미지)으로 렌더링 된 경로의 경우엔 각 `fetch` 요청은 독립적으로 재검증 됩니다.

 

주문형 재검증 (예시)

데이터는 요청 시 서버 액션 또는 라우트 핸들러에 있는 경로(revalidatePath) 또는 캐시 태그(revalidateTag)를 통해 재검증 될 수 있습니다.

 

Next.js는 라우트 전반에 걸쳐 `fetch` 요청을 무효화하기 위한 캐시 태깅 시스템이 있습니다.

 

1. `fetch` 를 사용할 때 하나 이상의 태그로 캐시 항목에 태그를 지정할 수 있는 옵션이 있습니다.

2. 그 후 revalidateTag를 호출하여 해당 태그와 연결된 모든 항목의 유효성을 다시 검사할 수 있습니다.

 

// app/page.tsx
export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

 

이 후, 서버 액션에서 revalidateTag를 호출해서 collection 태그가 지정된 `fetch` 요청을 재검증 할 수 있습니다.

 

// app/actions.ts
'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function action() {
  revalidateTag('collection')
}

 

오류 처리 및 재검증 (Error handling and revalidation)

데이터 재검증을 시도하는 동안 오류가 발생하면 마지막으로 성공적으로 생성된 데이터가 캐시에서 계속 제공됩니다. 다음 요청에서 Next.js는 데이터 재검증을 계속 시도합니다.

데이터 캐싱 선택 해제

아래의 표의 경우 `fetch` 요청이 캐시되지 않습니다.

내용
1. `fetch` 요청에 추가한 `cache: 'no-store'`
2. 각 `fetch` 요청마다 추가한 `revalidate: 0`
3. POST 메서드를 사용하는 라우트 핸들러의 `fetch` 요청
4. `headers`, `cookies`를 사용한 후의 `fetch` 요청
5. `const dynamic = 'force-dynamic'` 라우트 세그먼트 옵션이 사용된 경우
6. 기본값으로 fetchCache 라우트 세그먼트 옵션이 skip cache 하도록 설정된 경우
7. `fetch` 요청이 Authorization 또는 Cookie 헤더를 사용하고 상위 컴포넌트 트리에서 캐시되지 않은 요청이 있는 경 

 

개별 `fetch` 요청

개별 `fetch` 요청에 대한 캐싱을 선택 해제하려면 아래와 같이 설정하면 됩니다.

// layout.js | page.js
fetch('https://...', { cache: 'no-store' })

다중 `fetch` 요청

라우트 세그먼트에 여러 개의 `fetch` 요청이 있는 경우 세그먼트 구성 옵션을 사용해서 세그먼트에 있는 모든 데이터 요청의 캐싱 동작을 구현할 수 있습니다.

그러나, 개별 `fetch`요청의 캐싱 동작을 개별적으로 구성하는 것이 더 좋습니다. 이를 통해 캐싱 동작을 보다 세부적으로 제어할 수 있습니다.

서버에서 서드파티 라이브러리를 사용하는 데이터 패칭 / 캐싱 / 재검증

작성중 ...