한 줄 요약: React cache 함수는 서버 컴포넌트에서 같은 인자로 호출한 비동기 함수의 결과를 한 번의 서버 요청 동안 재사용하게 해 줍니다. 컴포넌트마다 같은 데이터를 요청해도 DB 조회는 한 번만 일어나므로, props를 깊게 내리지 않고도 중복 요청을 줄일 수 있습니다. 컴포넌트 바깥에서 한 번만 감싸고, 인자는 문자열이나 숫자 같은 원시값으로 넘기는 것이 핵심입니다.
React cache 함수란 무엇인가요?
React cache는 함수의 결과를 인자 기준으로 메모이제이션하는 React의 API입니다. 서버 컴포넌트(React Server Components) 전용으로 설계되어 있고, 캐시는 서버 요청 하나가 끝나면 무효화됩니다. 즉 사용자 A의 요청에서 만든 결과가 사용자 B의 요청에 섞이지 않습니다.
서버 컴포넌트는 useMemo나 useState 같은 상태 훅을 쓸 수 없습니다. 그래서 여러 컴포넌트가 같은 사용자 정보를 필요로 할 때 부모에서 한 번 조회해 props로 내려보내거나, 각자 조회하면서 중복 요청을 감수해야 했습니다. cache를 쓰면 각 컴포넌트가 필요한 데이터를 직접 요청해도 실제 호출은 한 번으로 합쳐집니다.
cache 기본 사용법은 어떻게 되나요?
데이터를 가져오는 비동기 함수를 cache로 감싸 모듈 최상위에서 내보냅니다. 이렇게 만든 함수를 여러 서버 컴포넌트에서 가져다 쓰면 됩니다.
// lib/data.ts
import { cache } from 'react';
import { db } from './db';
export const getUser = cache(async (id: string) => {
console.log('DB 조회:', id);
return db.user.findUnique({ where: { id } });
});
이제 페이지와 자식 컴포넌트가 각각 getUser를 호출해 보겠습니다.
// UserPage.tsx (서버 컴포넌트)
import { getUser } from './lib/data';
export default async function UserPage({ userId }: { userId: string }) {
const user = await getUser(userId); // 첫 호출: DB 조회
return (
<main>
<h1>{user?.name}</h1>
<Profile userId={userId} />
</main>
);
}
async function Profile({ userId }: { userId: string }) {
const user = await getUser(userId); // 같은 요청 안이라 캐시 재사용
return <p>{user?.email}</p>;
}
같은 userId로 렌더링되는 동안 getUser는 한 번만 실행되고, 콘솔에도 "DB 조회"가 한 번만 찍힙니다. 두 번째 호출은 첫 호출이 만든 Promise 결과를 그대로 돌려받습니다. 데이터가 필요한 곳에서 직접 가져오는 구조라 컴포넌트를 옮기거나 분리해도 props 연결을 고칠 필요가 없습니다.
cache는 언제 사용하면 좋은가요?
같은 렌더링 안에서 여러 서버 컴포넌트가 동일한 데이터를 필요로 할 때 사용합니다. 대표적인 경우는 다음과 같습니다.
- 레이아웃, 페이지, 메타데이터 생성 함수가 같은 사용자나 상품 정보를 각각 조회할 때
- fetch가 아닌 DB 클라이언트, ORM, 외부 SDK 호출을 중복 없이 쓰고 싶을 때
- 무거운 계산 결과를 같은 요청 안에서 공유하고 싶을 때
Next.js에서는 같은 URL과 옵션의 fetch 요청이 렌더링 중 자동으로 메모이제이션됩니다. 따라서 fetch를 쓴다면 별도 처리가 필요 없고, DB나 SDK처럼 fetch가 아닌 호출에 cache를 적용한다고 이해하면 됩니다.
cache, useMemo, memo는 무엇이 다른가요?
| 구분 | 실행 위치 | 무엇을 아끼나요? | 유지 범위 |
|---|---|---|---|
cache | 서버 컴포넌트 | 같은 인자의 함수 호출 결과 | 서버 요청 하나 |
useMemo | 클라이언트 컴포넌트 | 렌더링 사이의 계산 결과 | 컴포넌트 인스턴스 |
memo | 클라이언트 컴포넌트 | props가 같을 때 리렌더링 | 컴포넌트 인스턴스 |
세 가지는 이름이 비슷하지만 목적이 다릅니다. useMemo와 memo는 클라이언트에서 리렌더링 비용을 줄이는 도구이고, cache는 서버에서 같은 요청 안의 중복 호출을 줄이는 도구입니다.
cache 사용 시 주의할 점은 무엇인가요?
가장 흔한 실수는 컴포넌트 안에서 cache를 호출하는 것입니다. cache를 호출할 때마다 별도의 캐시가 만들어지므로, 렌더링마다 새 캐시가 생겨 공유 효과가 사라집니다. 인자도 주의해야 합니다. 원시값은 값으로 비교되지만 객체는 참조로 비교되어, 내용이 같아도 새 객체를 넘기면 캐시를 놓칩니다.
// 잘못된 예: 컴포넌트 안에서 cache를 호출
async function Profile({ userId }: { userId: string }) {
const getUser = cache(fetchUser); // 렌더링마다 새 캐시가 생김
const user = await getUser(userId);
return <p>{user.email}</p>;
}
// 잘못된 예: 객체 인자는 참조가 다르면 캐시 미스
await getUser({ id: '42' });
await getUser({ id: '42' }); // 서로 다른 객체라서 다시 조회
cache로 감싼 함수는 모듈 최상위에서 한 번만 만들고 여러 컴포넌트에서 가져다 씁니다.- 인자는 문자열, 숫자 같은 원시값을 사용합니다.
- 컴포넌트 바깥(예: 라우트 핸들러)에서 호출하면 함수는 실행되지만 캐시를 읽거나 쓰지는 않습니다.
- 캐시는 요청 단위로 비워지므로 요청 사이에 데이터를 유지하려면 별도의 데이터 캐시를 사용해야 합니다.
preload 패턴은 어떻게 쓰나요?
preload 패턴은 데이터를 쓰는 컴포넌트보다 먼저 요청만 시작해 두는 방법입니다. cache가 같은 Promise를 돌려주기 때문에, 부모에서 await 없이 호출해 두면 자식이 나중에 같은 요청을 이어받습니다.
// lib/data.ts
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
export function preloadUser(id: string) {
void getUser(id); // await 하지 않고 요청만 먼저 시작
}
// UserPage.tsx
export default async function UserPage({ userId }: { userId: string }) {
preloadUser(userId); // 다른 작업보다 먼저 조회 시작
const posts = await getPosts(userId);
return <Profile userId={userId} posts={posts} />;
}
async function Profile({ userId, posts }: { userId: string; posts: Post[] }) {
const user = await getUser(userId); // 이미 시작된 요청을 그대로 받음
return <h1>{user?.name} ({posts.length})</h1>;
}
이렇게 하면 getPosts를 기다리는 동안에도 사용자 조회가 이미 진행되므로, 순차 대기로 생기는 워터폴을 줄일 수 있습니다.
자주 묻는 질문(FAQ)
Q. cache는 클라이언트 컴포넌트에서도 쓸 수 있나요?
A. cache는 서버 컴포넌트용입니다. 클라이언트에서 값을 재사용하려면 useMemo나 데이터 페칭 라이브러리의 캐시를 사용하세요.
Q. 요청이 끝나면 캐시는 어떻게 되나요?
A. React가 서버 요청마다 모든 cache 함수의 캐시를 무효화합니다. 다음 요청은 새로 조회합니다.
Q. 인자가 없는 함수도 cache로 감쌀 수 있나요?
A. 네. 인자가 없으면 같은 요청 안에서 한 번만 실행되고 결과를 공유합니다. 설정값이나 현재 사용자 조회에 유용합니다.
Q. Next.js fetch 자동 메모이제이션이 있는데 cache가 또 필요한가요?
A. fetch만 쓴다면 필요 없습니다. DB 쿼리나 SDK 호출처럼 fetch가 아닌 비동기 함수에 cache를 적용하세요.
정리
cache는 서버 컴포넌트에서 같은 인자의 비동기 함수 결과를 요청 하나 동안 재사용합니다.- 함수는 컴포넌트 바깥, 모듈 최상위에서 한 번만 감쌉니다.
- 인자는 원시값을 쓰고 객체는 참조가 달라 캐시를 놓칠 수 있다는 점을 기억합니다.
- preload 패턴을 더하면 요청을 앞당겨 워터폴을 줄일 수 있습니다.
공식 문서: React 공식 문서 cache, React 공식 문서 Server Components
관련 글: React Server Components 기초와 use client 사용법, React Server Actions 사용법: use server 폼 처리
'React' 카테고리의 다른 글
| React preload preinit 사용법: 리소스 미리 로딩하기 (0) | 2026.10.19 |
|---|---|
| React Suspense 스트리밍 SSR 사용법 정리 (0) | 2026.10.19 |
| React Server Actions 사용법: use server 폼 처리 (0) | 2026.10.18 |
| React 19 Actions 사용법: useTransition으로 비동기 처리 (0) | 2026.10.17 |
| React useEffect 줄이는 법: 파생 상태와 key 리셋 정리 (0) | 2026.10.17 |