Next.js(App Router)를 활용하여 프로젝트를 진행하다 보면, 서버 컴포넌트(Server Component)에서 데이터를 가져올 때 어떤 방식으로 캐싱이 이루어지는지 혼란스러울 수 있다. 특히 fetch 옵션(force-cache, no-store, revalidate)에 따라 SSR, SSG, ISR이 어떻게 구현되는지, 그리고 데이터베이스를 직접 호출할 경우에는 어떤 차이가 있는지가 중요한 쟁점이 된다.
이 글은 이러한 질문에서 출발하여, Next.js 서버 컴포넌트에서의 데이터 패칭과 캐싱 동작을 SSR·SSG·ISR 관점에서 정리한 것이다.
질문 1.
서버 컴포넌트에서 fetch 함수에 force-cache 옵션을 부여하고 빌드를 수행하면, 빌드 시점에 해당 경로로부터 데이터를 가져와 결과물에 포함시키는가? 그리고 이후 클라이언트가 해당 페이지에 접근하더라도 서버에서는 더 이상 fetch 요청을 수행하지 않는가?
✅ 답변 1.
그렇다.
- force-cache는 SSG(Static Site Generation) 방식으로 동작한다.
- 즉, 빌드 시점에 한 차례 데이터를 요청하여 HTML 결과물에 삽입하고, 이후 클라이언트 접근 시에는 서버가 네트워크 요청을 다시 수행하지 않는다.
- 따라서 데이터가 변경되더라도 반영되지 않으며, 이를 갱신하기 위해서는 새로운 빌드를 수행해야 한다.
질문 2.
서버 컴포넌트에서 fetch를 사용할 때, 캐싱 옵션을 지정하지 않으면 기본값은 no-store인가?
✅ 답변 2.
아니다.
- 서버 컴포넌트에서 fetch의 기본 동작은 force-cache, 즉 SSG이다.
- 따라서 옵션을 생략하면 기본적으로 정적 캐싱이 적용된다.
- 만약 SSR(Server-Side Rendering) 이 필요하다면 cache: "no-store"를 명시해야 한다.
- 또한 ISR(Incremental Static Regeneration) 이 필요하다면 { next: { revalidate: n } } 형태로 설정해야 한다.
질문 3.
만약 서버 컴포넌트에서 API를 호출하는 것이 아니라, 데이터베이스로부터 직접 데이터를 가져오는 코드를 작성한다면 이 경우도 force-cache가 기본으로 적용되는가?
✅ 답변 3.
그렇지 않다.
- 데이터베이스를 직접 호출하는 경우(await db.query(...))에는 Next.js의 fetch 캐싱 옵션이 적용되지 않는다.
- 이러한 쿼리는 항상 런타임 시점에서 실행되며, 이는 곧 SSR 방식과 동일하다.
- 캐싱을 적용하고자 할 경우, Next.js에서 제공하는 unstable_cache, revalidatePath, revalidateTag와 같은 별도의 서버 캐싱 기능을 활용해야 한다.
질문 4.
Next.js의 다이나믹 라우트를 사용하면 그 때마다 최신 데이터(params, searchParams 등등)를 받아와야 하니 이건 자동으로 SSR 동작이 되는건가?
✅ 답변 3.
그렇다.
- app/[id]/page.tsx 같은 다이나믹 라우트는 URL마다 다른 데이터를 보여줘야 한다.
- Next.js는 이 경우 정적으로 미리 빌드(SSG) 할 수 없으므로, 런타임 요청마다 새로 렌더링(SSR) 한다.
- 따라서 이 경우에는 fetch의 기본값이 force-cache라고 하더라도, 라우트가 동적 파라미터에 의존하기 때문에 Next.js가 자동으로 no-store → SSR 처리를 한다.
하지만! https://nextjs.org/docs/app/api-reference/functions/generate-static-params 의 내용에 따르면
다이나믹 라우트 + generateStaticParams를 활용한다면 빌드 타이엠 다이나믹한 경로를 미리 지정하기 때문에 이 경우엔 SSG가 기본으로 적용되고 revalidate를 지정하면 ISR이 된다.
🚦 종합 정리
사용 방식기본 동작대응되는 렌더링 방식캐싱 여부
| 서버 컴포넌트 + fetch (옵션 없음) | force-cache | SSG | 빌드 시점 캐싱 |
| 서버 컴포넌트 + fetch + no-store | 항상 새 요청 | SSR | 캐싱 없음 |
| 서버 컴포넌트 + fetch + revalidate: n | 일정 주기마다 갱신 | ISR | 제한적 캐싱 |
| DB 직접 호출 (db.query) | 런타임마다 실행 | SSR | 캐싱 없음 (직접 구현 필요) |
✨ 결론
- 서버 컴포넌트에서 fetch를 사용할 경우, 옵션에 따라 SSG·SSR·ISR이 구현된다.
- 아무 옵션도 지정하지 않으면 기본값은 SSG(force-cache) 이다.
- no-store를 지정하면 SSR, revalidate를 지정하면 ISR 방식이 된다.
- 데이터베이스 직접 호출은 fetch 캐싱과 무관하게 항상 SSR로 동작하며, 별도의 캐싱 전략을 적용하지 않는 한 매번 쿼리를 수행한다.
따라서 데이터의 성격에 따라 SSG, SSR, ISR 중 적절한 전략을 선택하는 것이 성능 최적화와 데이터 일관성을 모두 확보하는 핵심이다.
* 추가:
서버 컴포넌트에서 fetch를 통해 현재 도메인/api/ route.ts 로 네트워크 요청을 하고 route.ts에선 prisma를 통해 디비의 데이터를 가져온다면 이 경우에도 force-cache가 적용되어 빌드 시점에 디비의 데이터를 받아오고 이후 최신 데이터는 반영하지 못함. 때문에 새롭게 빌드를 진행하여 데이터에 접근 하던지 cache 옵션을 줘서 ssr / isr로 작동하여 최신 데이터를 받아오도록 해야함
'FrontEnd > Next.js' 카테고리의 다른 글
| Next.js 로 블로그 개발하면서 겪었던 트러블 슈팅 모음 (0) | 2025.10.01 |
|---|---|
| 아이템 리스트 페이지의 서버 컴포넌트를 최적화 시켜보기(fetch 캐싱 옵션) (0) | 2025.09.19 |
| Resend로 발송 시 개발 모드에서 이메일이 오지 않는 경우 (0) | 2025.08.19 |
| [next.js] next/headers cookies로 set을 했지만 이후 undefined가 뜨는 이유 (0) | 2025.07.24 |
| 모노레포의 공통 ui 컴포넌트에 tailwind 적용 안될 때 (turborepo, tawilwind) (0) | 2024.12.11 |