리액트 19에 새로운 cache() API가 도입되었습니다, 이는 React Server Components(RSC)
에서 데이터 페칭과 계산 결과를 캐싱하는 데 사용됩니다.
각 렌더링 시 데이터 페칭 결과를 캐싱(메모이제이션) 할 수 있게 하며, 동일한 데이터를 여러 컴포넌트에서 페칭 할 때 데이터 결합도를 줄이는데 유용합니다.
1. 기본적인 사용법
컴포넌트 외부에서 `cache`를 호출해 캐싱기능을 가진 함수를 만들 수 있습니다.
(컴포넌트 내부에서 감쌀 경우 렌더링마다 새 함수가 생성된다.)
import {cache} from 'react';
import calculateMetrics from 'lib/metrics';
const getMetrics = cache(calculateMetrics);
function Chart({data}) {
const report = getMetrics(data);
// ...
}
getMetrics가 처음 data를 호출할 때, getMetrics는 calculateMetrics(data)를 호출하고 캐시에 결과를 저장하고
다시 getMetrics가 data와 함께 호출되면 이번엔 캐싱된 결과를 반환합니다.
2. 고비용 연산 캐싱
import {cache} from 'react';
import calculateUserMetrics from 'lib/user';
const getUserMetrics = cache(calculateUserMetrics);
function Profile({user}) {
const metrics = getUserMetrics(user);
// ...
}
function TeamReport({users}) {
for (let user in users) {
const metrics = getUserMetrics(user);
// ...
}
// ...
}
Profile이 먼저 렌더링 된다면 getUserMetrics(user)가 호출되면 캐싱된 결과가 있는지 확인하고, 캐시에 결괏값을 저장합니다. TeamReport에서는 캐시 된 결괏값을 읽어옵니다.
3. ex
// Temperature.js
import {cache} from 'react';
import {calculateWeekReport} from './report';
export function Temperature({cityData}) {
// 🚩 Wrong: 컴포넌트에서 `cache`를 호출하면 각 렌더링에 대해 `getWeekReport`가 생성됩니다.
const getWeekReport = cache(calculateWeekReport);
const report = getWeekReport(cityData);
// ...
}
// Precipitation.js
import {cache} from 'react';
import {calculateWeekReport} from './report';
// 🚩 Wrong: `getWeekReport`는 `Precipitation` 컴포넌트에서만 적용할 수 있습니다.
const getWeekReport = cache(calculateWeekReport);
export function Precipitation({cityData}) {
const report = getWeekReport(cityData);
// ...
}
Temperature의 경우 컴포넌트 내에서 cache가 있어 렌더링때마다 새로운 메모화된 함수가 생성된다.
두 컴포넌트에서 같은 메모화된 함수를 호출하려면
// getWeekReport.js
import {cache} from 'react';
import {calculateWeekReport} from './report';
export default cache(calculateWeekReport);
// Temperature.js
import getWeekReport from './getWeekReport';
export default function Temperature({cityData}) {
const report = getWeekReport(cityData);
// ...
}
// Precipitation.js
import getWeekReport from './getWeekReport';
export default function Precipitation({cityData}) {
const report = getWeekReport(cityData);
// ...
}
./getWeekReport.js 로 부터 export 해 온 같은 메모화된 함수를 호출합니다.
4. 데이터의 스냅샷 공유
import {cache} from 'react';
import {fetchTemperature} from './api.js';
const getTemperature = cache(async (city) => {
return await fetchTemperature(city);
});
async function AnimatedWeatherCard({city}) {
const temperature = await getTemperature(city);
// ...
}
async function MinimalWeatherCard({city}) {
const temperature = await getTemperature(city);
// ...
}
AnimatedWeatherCard와 MinimalWeatherCard에 같은 city가 들어오면 메모화된 함수로부터 같은 데이터의 스냅샷을 받고 다른 city를 인수로 받으면 fetchTemperature가 두 번 호출되고 호출마다 다른 데이터를 받게 됩니다.
city가 캐시 key처럼 동작된다.
5. 컴포넌트 외부에서는 캐시가 사용되지 않습니다.
import {cache} from 'react';
const getUser = cache(async (userId) => {
return await db.user.query(userId);
});
// 🚩 Wrong: 컴포넌트 외부에서 메모화된 함수를 호출하면 메모화하지 않습니다.
getUser('demo-id');
async function DemoProfile() {
// ✅ Good: `getUser`는 메모화 됩니다.
const user = await getUser('demo-id');
return <Profile user={user} />;
}
6. 프리로드 패턴(사전에 데이터 받아두기)
const getUser = cache(async (id) => {
return await db.user.query(id);
});
async function Profile({id}) {
const user = await getUser(id);
return (
<section>
<img src={user.profilePic} />
<h2>{user.name}</h2>
</section>
);
}
function Page({id}) {
// ✅ Good: 사용자 데이터 가져오기를 시작합니다.
getUser(id);
// ... 몇몇의 계산 작업들
return (
<>
<Profile id={id} />
</>
);
}
getUser(id)에 await를 사용하게 되면 데이터를 가져오기 전까지 UI 렌더링이 차단될 수 있다.
이 프리로드 패턴의 주의사항은 불필요한 데이터페칭을 할 수 있습니다. 최악의 경우 전혀 사용되지 않는 프리로드 데이터 페칭이 발생됩니다.
그러므로 프리로딩 패턴을 추가할 때 신중하게 고려할 필요가 있습니다. 모든 곳에 미리 추가하기보다는 특정 성능 문제가 있어서 해결이 필요할 때만 사용하세요.
또 한 가지 알아야 할 점은 Next.js에서 fetch() API를 사용할 때 이 데이터는 렌더링 시마다 캐시/메모화 됩니다. 그래서 여러 컴포넌트에서 동일한 데이터를 가져오거나 데이터를 미리 로드하기 위해 fetch() API를 사용하는 경우, cache()로 래핑 할 필요가 없습니다. cache() API는 주로 데이터베이스를 통해 가져오거나 다른 사용자 정의 데이터나 계산을 실행할 때도 유용합니다.
핵심 요약
- cache() API는 데이터 페칭이나 연산 결과를 렌더링 할 때마다 캐싱할 수 있게 합니다.
- cache() API는 데이터 결합도를 줄이고 컴포넌트 합성을 유지하는 데 유용합니다.
- 여러 컴포넌트에서 동일 데이터를 페칭 하는 경우 cache() API를 고려해야 합니다.
- cache() API를 사용하면 데이터를 미리 로드할 수 있고 깊이가 깊은 컴포넌트에서 프리로드된 데이터를 재사용하고 순차적 데이터 요청 문제를 해결할 수 있어서 성능이 향상됩니다. 단 프리로드 함수에 await를 사용하지 않도록 주의하세요.
- 컴포넌트 계층에서 불필요한 복잡성을 피하려면 프리로드 패턴을 사용할 시기와 위치를 신중히 고려하는 게 중요합니다.
참조
리액트 19의 cache() API로 서버 컴포넌트의 순차적 데이터 페칭 피하기
원문: https://aurorascharff.no/posts/avoiding-server-component-waterfall-fetching-with-react-19-cache/
siosio3103.medium.com
React
React is the library for web and native user interfaces. Build user interfaces out of individual pieces called components written in JavaScript. React is designed to let you seamlessly combine components written by independent people, teams, and organizati
react.dev
'react' 카테고리의 다른 글
Next.js SSR과 Zustand 사용시 주의점 (1) | 2025.05.17 |
---|---|
MobX는 왜 선택을 받았을까? (1) | 2025.05.15 |
React의 메모이제이션: cache, memo, useMemo 비교하기 (0) | 2025.04.12 |