-
Next.js App Router(4) - 데이터 패칭 전략Next.js 2025. 6. 30. 10:05728x90반응형
이번 글에서는 Next.js App Router에서는 데이터 패칭에 대하여 알아보겠습니다.
1. 왜 기본 fetch인가?
Next.js는 fetch 최적화를 전제로 설계되어 있습니다.
그래서 기본 fetch에 다음과 같은 기능이 붙어 있습니다.- 서버 캐싱 (Data Cache)
- Revalidate 옵션 (ISR)
- Streaming & Suspense와 자연스러운 통합
- 중복 요청 자동 방지 (Request Memoization)
반면 axios나 ky 등의 외부 라이브러리는 이러한 기능과 연결되지 않아, "Next.js가 제공하는 캐싱/스트리밍을 제대로 안 쓰는 것"과 같습니다.
결국 기본 fetch를 사용하면 의존성도 줄고, 성능 최적화도 그대로 가져갈 수 있습니다.
2. fetch의 기본 동작 방식: auto no-cache
Next.js v14에서 v15로 업데이트되며 fetch의 기본 캐싱 방식도 auto no-cache로 변경되었습니다.
이전에는 force-cache를 기본 값으로 사용했는데요.
이는 분명 캐시 측면에서는 강점이 있지만 이는 실시간 데이터를 다룰 때 부적합했습니다.
이 변경은 정적인 데이터와 동적인 데이터를 보다 명확히 구분해 주고, 불필요한 캐싱을 방지해 데이터를 최신 상태로 유지할 수 있게 변경되었습니다.
- force-cache: 명시적으로 캐싱을 원할 때 사용하며, SSG 효과가 있습니다.
- no-store: 매 요청마다 새롭게 데이터를 가져오는 SSR 방식으로 동작합니다.
- revalidate: 일정 주기마다 데이터를 갱신(ISR)합니다.
// 기본(auto no-cache) const res = await fetch('https://api.example.com/posts'); // 명시적 SSG 예시 - 데이터가 자주 바뀌지 않는 경우 const res = await fetch('https://api.example.com/posts', { cache: 'force-cache' }); // SSR 예시 const res = await fetch('https://api.example.com/posts', { cache: 'no-store' }); // ISR 예시 const res = await fetch('https://api.example.com/posts', { next: { revalidate: 60 } });
여기에 상황에 따라 revalidate 값을 조정하거나 React의 cache를 사용하여 중복 호출을 방지할 수 있습니다.
3. 중복 요청 방지: React의 Request Memoization와 cache
fetch를 사용한다면 Request Memoization처리를 통해 동일 URL을 여러 곳에서 fetch 해도, React 트리 내부에서는 한 번만 요청됩니다.
만약 DB 호출을 하거나 외부 SDK에서는 cache() 유틸을 사용하는 것으로 중복 호출을 막을 수 있습니다.
4. 클라이언트 데이터 패칭 - React Query, use()
use를 활용해 React 18에서 추가된 Streaming의 장점도 살리거나, React Query를 활용하는 것도 좋은 방법입니다.
이전과 같이 데이터 패칭을 React Query에 온전히 의존하기보다는 다음과 같이 확실한 기준에 따라 책임 영역을 분리하여 코드 유지보수성과 성능을 쉽게 챙길 수 있게 되었습니다.
- 서버 컴포넌트: 초기 렌더링, SEO, 정적 데이터
- 클라이언트 컴포넌트: 상호작용 기반, 자주 갱신되는 데이터 → React Query 활용
// app/page.tsx (Server Component) const userP = fetch('/api/user').then(r => r.json()); return ( <Suspense fallback={<div>Loading user…</div>}> <UserProfile promise={userP} /> <CommentsSection /> </Suspense> ); // app/components/UserProfile.tsx (클라이언트 컴포넌트) 'use client'; import { use } from 'react'; export function UserProfile({ promise }) { const user = use(promise); return <div>Welcome, {user.name}</div>; } // app/components/CommentsSection.tsx 'use client'; import { useQuery } from 'react-query'; export function CommentsSection() { const { data: comments } = useQuery('comments', () => fetch('/api/comments').then(r => r.json()) ); return comments.map(c => <div key={c.id}>{c.text}</div>); }
이런 흐름을 통해 다음과 같은 깔끔한 데이터 패칭 구조가 완성됩니다.
- 서버 fetch로 초기 데이터 가져오기 (use() + Suspense)
- 클라이언트 React Query로 추가 업데이트 처리
반응형'Next.js' 카테고리의 다른 글
Next.js App Router(3) - 폴더 구조와 파일 역할 (1) 2025.06.27 Next.js App Router(2) - 서버 vs 클라이언트 컴포넌트 (0) 2025.06.25 Next.js App Router(1) - Next.js의 새로운 패러다임 이해하기 (1) 2025.06.23 Next.js App Router Server Components와 Server Actions 알아보기 Part.3(Feat. react-hook-form, zod, react-query) (0) 2025.04.12 Next.js App Router Server Components와 Server Actions 알아보기 Part.2 (0) 2025.04.07