(번역) Our Journey With Caching
프론트엔드 성능은 제대로 구현하기 어렵습니다. 고도로 최적화도니 웹 어플리케이션에서도 가장 흔한 원인은 클라이언트와 서버 사이의 워터폴입니다. Next.js 앱 라우터를 도입할 때 이 문제를 해결하고 싶었습니다. 그러기 위해서는 클라이언트에서 수행하던 REST API 요청을 React Server Component를 활용해 서버에서 한번의 요청으로 처리하는 방식을 고안했습니다. 그러나 이를 위해서는 서버가 동적으로 동작해야 했고, 결과적으로 Jamstack의 우수한 초기 로딩 성능을 희생해야 하는 상황이 발생하였습니다. 이를 해결하기 위해 Partial Prerendering을 도입하여 정적성과 동적성을 모두 확보하였습니다.
하지만, 이 과정에서 개발자 경험(DX)가 저하되는 문제가 발생했습니다. fetch()
기본 동작을 캐싱 중심으로 변경하여 성능을 높였으나, 빠른 프로토타이핑과 동적 어플리케이션 개발이 불편해졌습니다. 또한, fetch()
가 아닌 로컬 데이터베이스 접근에 대한 명확한 캐싱 제어 방법도 부족했습니다. 이를 보완하기 위해 unstable_cache()
를 제공했지만 사용성이 직관적이지 않았습니다. 결국 이러한 문제를 해결하기 위해 export const dynamic, runtime, fetchCache, dynamicParams, revalidate = ...
와 같은 세그먼트 단위 설정이 추가되었습니다.
우리는 기존 방식도 하위 호환성을 유지하면서 계속 지원할 예정입니다. 하지만 보다 단순하고 직관적인 접근 방식이 필요하다고 판단했습니다. 이를 해결하기 위해, 현재 새로운 실험적 모드를 개발 중이며, 이는 <Suspense>
와 use cache
라는 두 가지 개념만으로 동작하도록 설계되었습니다.
모험을 선택하세요
가장 먼저 눈에 띄는 점은 컴포넌트에 데이터를 추가하면 오류가 발생한다는 것입니다.
async function Component() {
return fetch(...) // error
}
export default async function Page() {
return <Component />
}
이제 데이터, 쿠키, 헤더, 현재 시간 또는 임의의 값을 사용할 때 데이터를 캐시(서버 또는 클라이언트 측)할 것인지, 아니면 모든 요청에 대해 실행할 것인지 선택할 수 있습니다. 여기서는 fetch()를 예로 들었지만 데이터베이스나 타이머와 같은 모든 비동기 노드 API에 적용될 수 있습니다.