React 19 cache()로 서버 컴포넌트 워터폴 페칭 막기
원문: Avoiding Server Component Waterfall Fetching with React 19 cache()
서버 컴포넌트에서 데이터 요청이 부모→자식으로 순차적으로 이뤄져 전체 렌더링을 지연시키는 문제가 있는데, 이를 Waterfall Fetching이라 부른다.
해당 게시글은 이 문제를 간단히 짚고, React 19에서 새롭게 제공되는 cache() API를 활용해 이를 최소화하는 방법을 소개한다. 중복 요청을 방지하고 캐싱을 활용해 불필요한 대기 시간을 줄임으로써, 서버 컴포넌트의 성능을 한층 개선할 수 있다는 점이 핵심이다.
📚 React 19 cache()로 서버 컴포넌트 워터폴 페칭 방지하기
1단계: cache() API가 뭔가요?
React 19에서 새로 나온 cache() API는 데이터 페칭이나 계산 결과를 캐싱하는 기능입니다.
🎯 실생활 비유: 마트에서 장 볼 때를 생각해보세요. 가족 구성원 여러 명이 각자 우유가 필요해서 따로따로 마트에 간다면 비효율적이겠죠? cache()는 한 번 사온 우유를 가족이 함께 나눠 쓰는 것과 같습니다.
javascript
const getUser = cache(async (userId: string) => {
return db.getUser(userId);
});2단계: 왜 필요한가요? - 워터폴 문제
워터폴 페칭은 데이터를 순차적으로 가져오는 문제입니다.
🌊 워터폴 비유: 계단식 폭포처럼 데이터가 위에서 아래로 차례대로 흐릅니다:
- Post 컴포넌트가 데이터를 가져옴 (1초)
- Post가 끝나야 Comments가 시작 (또 1초)
- 총 2초 소요 😓
javascript
// 문제가 있는 코드
async function Post({ postId }) {
const post = await getPost(postId);// 1초 대기
return (
<div>
<h2>{post.title}</h2>
<Suspense fallback={<div>Loading comments...</div>}>
<Comments postId={postId} /> {/* Post 후에야 시작 */}
</Suspense>
</div>
);
}3단계: 해결 방법들
방법 1: Promise.all() 사용 (단순하지만 문제 있음)
javascript
const [post, comments] = await Promise.all([
getPost(postId),
getComments(postId)
]);문제점:
- 컴포넌트 간 결합도 증가
- Post가 Comments 데이터도 알아야 함
- 나중에 Comments 제거시 코드 정리 필요
방법 2: cache()로 프리로딩 (추천! ✨)
javascript
// 1. getComments를 cache()로 감싸기
const getComments = cache(async (postId: string) => {
// 데이터 페칭 로직
});
// 2. 상위 컴포넌트에서 미리 호출 (await 없이!)
export async function Post({ postId }) {
getComments(postId);// 프리로드 시작
const post = await getPost(postId);
return (
// Comments는 이미 로딩 중이므로 바로 렌더링
);
}4단계: 실제 효과
🚀 성능 개선:
- 이전: Post(1초) → Comments(1초) = 총 2초
- 이후: Post와 Comments 동시 시작 = 총 1초
5단계: 주의사항 및 베스트 프랙티스
- 숨겨진 결합 문제 해결:
javascript
// Comments 컴포넌트에서 프리로드 함수 export
export const preloadComments = (id: string) => {
void getComments(id);
};- Next.js fetch()는 이미 캐싱됨:
- fetch() API 사용시 cache() 불필요
- DB 직접 호출이나 커스텀 함수에 사용
- 언제 사용할까?:
- ✅ 여러 컴포넌트에서 같은 데이터 필요할 때
- ✅ 서버 컴포넌트 워터폴 방지할 때
- ❌ 성능 문제가 없다면 미리 최적화 금지
- 컴포지션 vs 퍼포먼스 트레이드오프
- cache()는 컴포넌트 독립성을 유지하면서도 성능을 개선하는 절묘한 해결책입니다
- 기존에는 둘 중 하나를 포기해야 했죠
- React의 진화 방향
- React는 개발자 경험(DX)과 성능을 동시에 잡으려 노력하고 있습니다
- cache()는 이런 철학을 잘 보여주는 API입니다
- 실무 적용 팁
- 먼저 측정하고, 실제 병목이 있을 때만 최적화하세요
- 프리로딩 패턴은 강력하지만, 코드 복잡도를 높일 수 있습니다
- 팀원들과 패턴을 공유하고 문서화하세요
- Next.js vs 다른 프레임워크
- React Router v7, TanStack Start는 loader 패턴으로 자동 해결
- Next.js는 더 명시적인 제어를 제공하는 대신 수동 작업 필요
이 cache() API는 React 서버 컴포넌트의 성능을 크게 개선할 수 있는 도구입니다. 특히 복잡한 데이터 의존성이 있는 대규모 애플리케이션에서 빛을 발할 것 같네요! 😊