확장된 fetch
API
Next.js 15에서는 기본 fetch
API를 확장하여 캐싱 및 재검증 동작을 세밀하게 제어할 수 있다. fetch
함수의 두 번째 인자로 next
옵션을 전달하여 다음과 같은 설정이 가능하다:
cache
: 캐싱 전략 설정 ('force-cache'
,'no-store'
등)revalidate
: 캐시된 데이터를 재검증하는 시간(초 단위)tags
: 캐시 태그를 지정하여 그룹화된 데이터의 재검증에 활용
예시:
const res = await fetch('https://api.example.com/data', {
next: {
cache: 'force-cache',
revalidate: 3600,
tags: ['example-data'],
},
});
이 설정은 해당 요청의 응답을 1시간 동안 캐시하며, 'example-data'
태그를 통해 그룹화된 데이터의 재검증에 활용할 수 있다.
revalidateTag
: 태그 기반의 온디맨드 재검증
revalidateTag
함수는 지정된 태그와 연관된 모든 캐시된 데이터를 무효화하여 다음 요청 시 최신 데이터를 가져오도록 한다. 이는 서버 액션이나 라우트 핸들러에서 호출할 수 있다.
예시:
// app/actions.ts
'use server';
import { revalidateTag } from 'next/cache';
export async function updateData() {
// 데이터 업데이트 로직
revalidateTag('example-data');
}
주의사항:
revalidateTag
는 해당 태그와 연관된 경로를 다음에 방문할 때 캐시를 무효화한다.- 즉시 모든 경로를 재검증하지 않으며, 다음 방문 시에만 적용된다.
- 태그는 문자열로 지정하며, 대소문자를 구분하고 256자 이하여야 한다.
revalidatePath
: 경로 기반의 온디맨드 재검증
revalidatePath
함수는 특정 경로의 캐시를 무효화하여 다음 요청 시 최신 데이터를 가져오도록 한다. 이 함수도 서버 액션이나 라우트 핸들러에서 호출할 수 있다.
예시:
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function updatePage() {
// 페이지 업데이트 로직
revalidatePath('/example/path');
}
옵션:
path
: 재검증할 경로를 문자열로 지정type
(선택):'page'
또는'layout'
을 지정하여 해당 유형의 경로를 재검증
주의사항:
revalidatePath
도 해당 경로를 다음에 방문할 때 캐시를 무효화한다.- 동적 경로의 경우, 정확한 경로를 지정해야 한다.
캐싱 기본 동작 변경
Next.js 15에서는 캐싱의 기본 동작이 변경되었다:
GET
메서드를 사용하는 라우트 핸들러는 기본적으로 캐시되지 않는다.- 명시적으로 캐싱을 설정하려면
export const dynamic = 'force-static'
과 같은 구성을 사용해야 한다.
예시:
// app/api/data/route.ts
export const dynamic = 'force-static';
export async function GET() {
// 데이터 반환 로직
}
요약
- 확장된
fetch
API를 통해 캐싱 및 재검증을 세밀하게 제어할 수 있다. revalidateTag
와revalidatePath
를 사용하여 태그 또는 경로 기반의 온디맨드 재검증이 가능하다.- Next.js 15에서는
GET
라우트 핸들러의 캐싱이 기본적으로 비활성화되어 있으며, 명시적으로 설정해야 한다.
CDN 캐시와 연산 캐시
CDN 캐시 (Content Delivery Network Cache)
-
정의: 최종 HTML, 이미지, JSON 응답 등을 지리적으로 분산된 서버(CDN)에서 캐싱하여 사용자에게 가까운 위치에서 빠르게 서빙하는 방식이다.
-
Next.js에서 사용 예시:
fetch(url, { next: { revalidate, tags } })
export const dynamic = 'force-static'
- SSG, ISR 기반 페이지
-
특징:
- 클라이언트 요청 → Vercel CDN → 캐시 hit 시 백엔드까지 가지 않음
X-Vercel-Cache: HIT
헤더로 확인 가능revalidateTag
,revalidatePath
로 무효화 가능
-
장점:
- 매우 빠름 (백엔드 연산 없이 전달)
- 글로벌 사용자에게 일관된 성능 제공
-
단점:
- 정적 리소스에 적합
- 사용자 인증이 필요한 컨텐츠에는 부적합
서버 연산 캐시 (Function-level Cache)
-
정의: 데이터베이스 쿼리, 복잡한 연산 결과 등을 서버에서 일정 시간 저장하고, 다음 요청부터는 연산을 생략하는 방식이다.
-
Next.js에서 사용 예시:
unstable_cache(async () => { ... }, key, { revalidate })
-
특징:
- fetch 없이도 캐싱 가능 (DB 연산, JSON 변환, 복합 연산 포함)
- 서버 빌드 타임 또는 런타임에서 저장됨
- 클라이언트가 직접 접근 불가
-
장점:
- 연산 비용 절감
- API 응답이나 HTML에 사용되지 않아도 캐시 활용 가능
-
단점:
- 캐시 위치가 CDN이 아니므로 지연시간은 약간 있음
- Vercel 이외 환경에서는 제한적
비교 항목 | CDN 캐시 | 서버 연산 캐시 (unstable_cache ) |
---|---|---|
적용 위치 | Vercel 엣지 서버 | SSR 서버 또는 Edge Functions 내부 |
대상 | HTML, JSON, 이미지, fetch 결과 | 함수 실행 결과, DB 쿼리 결과 |
캐시 무효화 방법 | revalidateTag , revalidatePath | revalidateTag , revalidatePath |
TTL 설정 | revalidate: number | revalidate: number |
사용 용도 | 외부에 노출되는 페이지/데이터 | 내부 연산 최적화, 서버 자원 절약 |
인증 데이터 | ❌ 기본적으로 부적합 | ⭕ 접근 제어된 데이터 처리 가능 |
의존성 | URL에 종속 | 키(key)와 파라미터에 종속 |
- 정적 API 응답이나 HTML 페이지
→fetch
+next.revalidate
+ CDN 캐시 - DB 쿼리, 복잡한 비즈니스 로직 연산 결과 캐싱
→unstable_cache
로 연산 자체를 줄이는 방향 - 로그인 사용자 전용 데이터, 세션 기반 처리
→ 캐시 없이 SSR 또는no-store
fetch 사용 (또는 edge 캐시 우회)
unstable_cache
()
unstable_cache
는 Next.js 내부에서 제공하는 서버 측 캐싱 함수로, 주로 데이터 페칭 함수에 적용하여 정적으로 서버에서 캐싱된 결과를 반환하도록 한다. 이름에 unstable
이 포함되어 있지만, 실제로는 작동한다.
사용 예
import { unstable_cache } from 'next/cache'
const getUserData = unstable_cache(
async (userId: string) => {
const res = await fetch(`https://api.example.com/users/${userId}`)
return res.json()
},
['user-data'],
{ revalidate: 3600 }
)
- 첫 번째 인자: 캐시할 함수
- 두 번째 인자: 캐시 키(key). 함수 인자 기반으로 캐시를 구분하려면 포함시켜야 함
- 세 번째 인자:
{ revalidate: number; tags?: string[] }
옵션
이 함수를 통해 서버에서 주기적으로 데이터가 재검증되고, SSR 중 재사용이 가능해진다.
fetch()
의 캐시 옵션과의 차이점
항목 | fetch() with next 옵션 | unstable_cache() |
---|---|---|
적용 대상 | HTTP 요청 (URL 기반) | 일반 함수 또는 fetch 함수 |
캐시 저장 위치 | Vercel CDN 또는 Edge Runtime | 서버 빌드 환경의 캐시 스토리지 |
키 결정 방식 | 요청 URL + next.tags 등 | 명시적인 키(key[] ) |
주 용도 | 외부 API 응답 캐싱 | DB 쿼리/로직 결과 자체 캐싱 |
호출 방식 | 매번 요청될 수 있음 | 캐시 히트 시 함수 자체 호출 생략 |
캐시 무효화 | revalidateTag , revalidatePath | 동일하게 사용 가능 (tags 제공 시) |
경고 사항 | 클라이언트에서 호출 시 next 옵션 무시 | 클라이언트에서는 사용 불가 (서버 전용) |
정리
fetch()
는 HTTP 요청을 캐싱할 수 있는 기능이다. 주로 외부 API 응답을 그대로 가져올 때 유용하다.unstable_cache()
는 함수의 실행 결과를 캐싱하며, fetch 외의 연산 결과도 저장할 수 있다. 특히 DB 쿼리나 복잡한 서버 연산을 반복 실행하지 않게 할 때 유용하다.- 둘 다
revalidate
,tags
를 통해 캐시 생명주기를 제어할 수 있으며,revalidateTag
등을 통해 수동 무효화도 가능하다. - CDN 레벨에서의 퍼포먼스를 고려한다면 fetch + CDN 캐시가 더 적합하고, 서버 연산 최소화에 초점을 둔다면
unstable_cache()
가 더 적합하다.