개요
cache
는 React Server Components(서버 컴포넌트)에서만 사용할 수 있는 API로, 데이터 패칭이나 연산 결과를 캐싱(메모이제이션)할 수 있게 해줍니다. 동일한 인자로 호출 시 중복 연산을 방지하고, 여러 컴포넌트 간에 결과를 공유할 수 있습니다.
const cachedFn = cache(fn);
Reference
cache(fn)
컴포넌트 외부에서 cache
를 호출해, 주어진 함수의 결과를 캐싱하는 버전을 만듭니다.
- 파라미터
fn
: 캐싱할 함수. 어떤 인자든 받을 수 있으며, 반환값도 자유롭습니다.
- 반환값
- 동일한 시그니처의 캐싱된 함수. 인자 조합별로 결과를 저장하며, 캐시 미스 시에만 원본 함수를 호출합니다.
동작 방식
- 캐싱된 함수(
cachedFn
)를 호출하면, 동일한 인자로 이미 호출된 적이 있다면 캐시된 결과를 반환합니다. 없으면 원본 함수를 호출해 결과를 캐시에 저장 후 반환합니다. - 에러도 캐싱됩니다. 특정 인자에서 에러가 발생하면, 같은 인자로 호출 시 동일한 에러가 재발생합니다.
- 각 서버 요청마다 캐시가 무효화됩니다(요청 단위 캐시).
- 같은 함수로 여러 번
cache
를 호출하면, 각각 별도의 캐시를 가집니다. - 반드시 컴포넌트 외부(모듈 스코프)에서 캐싱 함수를 생성해야 여러 컴포넌트에서 캐시를 공유할 수 있습니다.
Caveats(주의사항)
- Server Components에서만 사용해야 하며, 클라이언트 컴포넌트에서는 동작하지 않습니다.
- 캐싱된 함수는 인자 참조(객체, 배열 등)에 따라 캐시 히트 여부가 달라집니다. 동일한 값이라도 참조가 다르면 캐시 미스가 발생합니다.
사용법
고비용 연산 캐싱
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 of users) {
const metrics = getUserMetrics(user);
// ...
}
}
- 동일한 user 객체라면 여러 컴포넌트에서 연산 결과를 공유합니다.
데이터 스냅샷 공유
import { cache } from 'react';
async function fetchData() {
return await fetch('https://...');
}
const getData = cache(fetchData);
async function MyComponent() {
getData(); // 첫 호출: fetch 시작, 프라미스 캐싱
// ...
await getData(); // 두 번째 호출: 캐시된 프라미스 사용
}
- 첫 호출에서 프라미스가 캐싱되고, 두 번째 호출에서는 같은 프라미스를 재사용합니다.
여러 컴포넌트에서 캐시 공유하기
- 반드시 모듈 스코프에서 캐싱된 함수를 만들어 여러 컴포넌트에서 import해 사용해야 캐시가 공유됩니다.
// 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);
// ...
}
Pitfall & 트러블슈팅
1. 서로 다른 캐싱 함수는 캐시를 공유하지 않음
- 같은 원본 함수라도 여러 번
cache
를 호출하면 각각 별도의 캐시를 가집니다. 반드시 하나의 캐싱 함수를 여러 컴포넌트에서 import해 사용하세요.
2. 컴포넌트 내부에서 cache 호출 시 캐시 공유 불가
- 컴포넌트 내부에서
cache
를 호출하면, 렌더마다 새로운 캐싱 함수가 생성되어 캐시가 공유되지 않습니다.
3. 객체/배열 등 참조값 인자 주의
- 인자가 객체/배열 등 참조 타입이면, 값이 같아도 참조가 다르면 캐시 미스가 발생합니다. 원시값(primitive) 인자를 사용하거나, 동일한 참조를 전달해야 합니다.
// 잘못된 예시
const calculateNorm = cache((vector) => { /* ... */ });
function MapMarker(props) {
const length = calculateNorm(props); // props는 매번 새 객체
}
// 올바른 예시
const calculateNorm = cache((x, y, z) => { /* ... */ });
function MapMarker(props) {
const length = calculateNorm(props.x, props.y, props.z); // 원시값 사용
}
useMemo, memo, cache 차이
- useMemo: 클라이언트 컴포넌트에서 렌더 간 동일한 연산을 캐싱(컴포넌트 인스턴스 단위, 공유 불가)
- memo: props가 바뀌지 않으면 컴포넌트 자체의 리렌더를 막음(컴포넌트 단위)
- cache: 서버 컴포넌트에서 여러 컴포넌트 간 연산/패칭 결과를 공유(요청 단위, 모듈 단위로 공유)
참고 및 주의사항
- cache는 반드시 Server Components에서만 사용해야 하며, 클라이언트 컴포넌트에서는 동작하지 않습니다.
- 캐싱된 함수는 인자 참조에 민감하므로, 동일한 참조를 전달하거나 원시값을 사용하는 것이 좋습니다.
- 각 서버 요청마다 캐시가 초기화됩니다.
반응형