THE DEVLOG

scribbly.

Next JS 치트 시트

2025.05.10 19:41:42

Route Segment Config

Next.js 15에서는 각 layout.tsx, page.tsx, route.ts 파일에서 export하는 설정값들을 통해 해당 세그먼트의 동작 방식을 세밀하게 제어할 수 있다. 이는 기존의 getStaticProps, getServerSideProps 모델보다 더 세분화된 캐싱 및 렌더링 제어가 가능하게 해준다.

Route Segment Config는 Next.js에서 개발중인 dynamicIO 기능으로 대체될 예정

설정 가능한 옵션 목록

옵션명타입기본값
experimental_pprboolean없음
dynamic'auto' | 'force-dynamic' | 'error' | 'force-static''auto'
dynamicParamsbooleantrue
revalidatefalse | 0 | numberfalse
fetchCache'auto' | 'default-cache' | 'only-cache' | 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store''auto'
runtime'nodejs' | 'edge''nodejs'
preferredRegion'auto' | 'global' | 'home' | string[]'auto'
maxDurationnumber배포 플랫폼에서 설정

주요 옵션

dynamic

해당 세그먼트를 정적으로 렌더링할지, 동적으로 렌더링할지를 설정한다.

export const dynamic = 'auto'

설명
'auto'가능한 한 정적으로 캐싱하되, 동적 API가 사용되면 자동으로 동적 렌더링으로 전환한다.
'force-dynamic'항상 SSR로 처리한다. fetch마다 { cache: 'no-store', next: { revalidate: 0 } }로 설정한 것과 같다.
'error'동적 API를 사용하면 에러를 발생시킨다. 캐싱만 허용. getStaticProps와 유사.
'force-static'정적 렌더링을 강제한다. cookies(), headers(), useSearchParams()는 빈 값을 반환한다.

🧠 참고:

  • 'error''force-static'을 사용하면 dynamicParams는 기본값이 false로 바뀐다.

export const dynamic = 'auto'인 경우:
Next.js는 정적 추론을 시도한다. fetch 요청이 모두 force-cache 등 정적 캐싱이면 정적 페이지로 처리하고 CDN에 저장한다.

dynamic = 'force-static':
완전한 정적 페이지로 간주. HTML, JSON, Static Asset이 Vercel CDN에 올라가고 변경 전까지는 그대로 유지된다.

dynamic = 'force-dynamic':
매 요청마다 서버에서 SSR 처리. CDN 캐시 없이 항상 fresh한 응답.

revalidate = 60:
CDN에 저장된 정적 페이지라도 60초마다 서버에서 다시 생성 가능함 (ISR). 이때 새 HTML이 생성되면 CDN도 새 것으로 덮어쓴다.

generateStaticParams

export async function generateStaticParams() {
  return [{ slug: 'example' }, { slug: 'another' }]
}

  • 동적 세그먼트에 대한 정적 경로 목록을 정의한다.
  • 빌드 타임에 static page를 생성하며, 이외는 런타임에 생성됨.

dynamicParams

동적 세그먼트에 대해 generateStaticParams에 포함되지 않은 경우의 동작을 결정한다.

export const dynamicParams = true

설명
true포함되지 않은 세그먼트는 런타임에 생성된다.
false해당 경로는 404를 반환한다.

🧠 참고:

  • getStaticPathsfallback 옵션을 대체한다.
  • generateStaticParams()가 빈 배열을 반환하면, 경로 방문 시 최초 1회만 static render된다.
  • dynamic = 'error'일 경우 자동으로 false로 바뀐다.

revalidate

페이지 또는 레이아웃의 기본 revalidate 주기를 설정한다.

export const revalidate = 3600 // 1시간

설명
false영구 캐싱 (Infinity)
0항상 동적 렌더링
숫자초 단위로 revalidate 주기 지정

🧠 참고:

  • revalidate = 60 * 10은 안 되고 revalidate = 600처럼 정적 분석 가능한 값이어야 한다.
  • runtime = 'edge'일 때는 사용 불가.
  • 개발 모드에서는 항상 동적으로 렌더링됨.

fetchCache

모든 fetch 요청의 기본 캐시 동작을 지정한다.

export const fetchCache = 'force-cache'

의미
'auto'알아서 처리
'default-cache'기본 캐싱 동작 사용
'only-cache'캐시된 데이터만 사용, 미스 시 실패
'force-cache'강제로 캐싱
'force-no-store'강제로 비캐싱
'default-no-store'기본 비캐싱 동작 사용
'only-no-store'캐시된 데이터 무시하고 항상 네트워크 요청

dynamicIO: 동적 데이터 기본 설정

dynamicIO는 Next.js 15에서 도입된 실험적 기능으로, App Router에서의 데이터 페칭을 기본적으로 프리렌더링에서 제외시킨다. 즉, 명시적으로 캐시되지 않은 데이터는 항상 런타임에 새로 페칭된다.

dynamicIO를 활성화하려면 next.config.ts 파일의 experimental 섹션에 다음과 같이 설정한다:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  experimental: {
    dynamicIO: true,
  },
};

export default nextConfig;

'use cache': 명시적인 캐싱 지시어

'use cache' 지시어는 특정 라우트, 컴포넌트, 또는 함수의 결과를 캐시하도록 명시하는 역할을 한다. 이 지시어를 사용하면 해당 부분의 데이터가 캐시된다.

  • 파일 수준 캐싱: 파일 상단에 'use cache'를 추가하여, 해당 파일 내의 모든 익스포트를 캐시한다.
  // app/page.tsx
  'use cache';

  export default async function Page() {
    const data = await fetchData();
    return <div>{data}</div>;
  }

  • 컴포넌트 수준 캐싱: 컴포넌트 내에서 'use cache'를 사용하여, 해당 컴포넌트의 결과를 캐시한다.
  export async function MyComponent() {
    'use cache';
    const data = await fetchData();
    return <div>{data}</div>;
  }

  • 함수 수준 캐싱: 특정 함수 내에서 'use cache'를 사용하여, 해당 함수의 반환값을 캐시한다.
  export async function getData() {
    'use cache';
    const response = await fetch('/api/data');
    return response.json();
  }

캐시된 함수의 반환값은 직렬화 가능해야 하며, 비직렬화 가능한 인자나 props는 캐시 키에 포함되지 않다. 또한, 기본적으로 서버 측 캐시는 15분마다 재검증되며, cacheLifecacheTag API를 사용하여 캐시의 수명을 조정하거나 태그를 지정할 수 있다.

cacheLife: 캐시 수명 설정

cacheLife 함수는 캐시된 데이터의 수명을 설정하는 데 사용된다. 이를 통해 데이터가 얼마나 오래 캐시되어야 하는지를 제어할 수 있다.

사용 예시:

import { unstable_cacheLife as cacheLife } from 'next/cache';

export async function getData() {
  'use cache';
  cacheLife('hours'); // 캐시 수명을 'hours'로 설정
  const response = await fetch('/api/data');
  return response.json();
}

cacheLife'seconds', 'minutes', 'hours', 'days', 'weeks', 'max'와 같은 문자열 값을 받아 캐시 수명을 설정한다. 또한, next.config.ts 파일에서 커스텀 캐시 프로파일을 정의하여 더 세밀한 제어가 가능하다.(Next.js)

예를 들어, next.config.ts에서 다음과 같이 설정할 수 있다:

// next.config.ts
const nextConfig = {
  experimental: {
    dynamicIO: true,
    cacheLife: {
      categories: {
        stale: 1800, // 클라이언트에서 30분 동안 캐시된 데이터 사용
        revalidate: 600, // 서버에서 10분마다 데이터 재검증
        expire: 86400, // 1일 후 캐시 만료
      },
    },
  },
};

이후, 함수 내에서 cacheLife('categories')를 호출하여 해당 프로파일을 적용할 수 있다.

cacheTag: 캐시 태그 지정 및 무효화

cacheTag 함수는 캐시된 데이터에 태그를 지정하여, 특정 태그를 가진 캐시를 선택적으로 무효화하거나 재검증할 수 있도록 한다.

사용 예시:

import { unstable_cacheTag as cacheTag } from 'next/cache';

export async function getCategories() {
  'use cache';
  cacheTag('categories-data'); // 'categories-data' 태그 지정
  const response = await fetch('/api/categories');
  return response.json();
}

이후, 서버 액션이나 API 라우트에서 revalidateTag를 호출하여 해당 태그를 가진 캐시를 무효화할 수 있다:

import { revalidateTag } from 'next/cache';

export async function revalidateCategories() {
  revalidateTag('categories-data'); // 'categories-data' 태그를 가진 캐시 무효화
}

이를 통해, 예를 들어 관리자 패널에서 새로운 카테고리를 추가하거나 기존 카테고리를 수정한 후, 해당 캐시를 즉시 무효화하여 최신 데이터를 반영할 수 있다.

요약

기능설명
dynamicIO데이터 페칭을 기본적으로 런타임에 수행하도록 설정하는 실험적 기능.
'use cache'특정 라우트, 컴포넌트, 또는 함수의 결과를 캐시하도록 명시하는 지시어.
cacheLife캐시의 수명을 설정하여, 일정 시간 후에 자동으로 재검증되도록 한다.
cacheTag캐시에 태그를 지정하여, 특정 태그의 캐시를 무효화하거나 재검증할 수 있다.

dynamicIO의 도입과 Route Segment Config의 대체

dynamicIO는 Next.js 15의 실험적 기능으로, 다음과 같은 방향으로 기존 Route Segment Config의 역할을 함수/컴포넌트 수준에서 재정의한다:

dynamic → 제거됨

  • export const dynamic = ... 은 더 이상 권장되지 않으며, 대신 함수나 컴포넌트 안에서 직접 'use cache', 'use dynamic' 등을 선언함.
// 예시
export default async function Page() {
  'use dynamic'; // 과거의 'force-dynamic'과 동일
  const data = await fetchData();
  return <div>{data}</div>;
}

revalidatecacheLife로 대체

  • export const revalidate = 60 같은 방식은 cacheLife() 함수로 대체되어, 코드 내부에서 세밀하게 설정 가능.
import { unstable_cacheLife as cacheLife } from 'next/cache';

export async function Page() {
  'use cache';
  cacheLife('minutes'); // 또는 cacheLife(60)
  ...
}

fetchCache → 제거됨

  • fetchCache는 이제 무의미. 대신 'use cache'cacheLife()가 이를 대체하고, fetch 자체는 명시적으로 설정 필요 없음.

dynamicParams → 무시됨

  • generateStaticParams()와 관련된 dynamicParams 설정도 사실상 무시됨. 동적 세그먼트에 대한 캐싱은 use cache 여부로만 결정됨.

dynamicIO가 Route Segment Config를 대체하는 방식

기존 ConfigdynamicIO 활성화 후 대체 방식
export const dynamic = ...'use dynamic', 'use cache' 지시어로 대체
export const revalidate = 60cacheLife(60) 호출로 대체
export const fetchCache = ...별도 옵션 없음. 'use cache'로 처리
export const dynamicParams = false더 이상 무의미. generateStaticParams()는 계속 사용됨

실제 코드 예시

// Next.js 15, dynamicIO 기준 코드

export default async function Page({ params }: { params: { id: string } }) {
  'use cache';
  cacheLife('minutes'); // 60초 동안 캐시

  const res = await fetch(`https://api.example.com/data/${params.id}`);
  const data = await res.json();

  return <div>{data.title}</div>;
}