프롤로그
유저 경험에서 빼놓을 수 없는 것이 ‘캐싱’이라고 생각하는데, 사용하고 있는 Next.js 전반에 걸쳐 어떤 방식으로 캐싱을 지원하고 있는지 좀 더 자세히 알아보고 싶어졌습니다.
그래서 오늘부터 총 네 가지의 캐싱 전략, 그 중에서도 ‘Request Memoization’에 대해서 알아보도록 하겠습니다.
요청 메모이제이션(Request Memoization)
리액트에서 동일한 데이터를 동일한 페이지의 여러 컴포넌트에서 표시해야 할 때 주로 발생하는 캐싱 방식입니다.
일반적으로는 위 상황을 구현하기 위해서는 ‘표시해야 하는 컴포넌트 마다 요청하여 N번 수행’ 또는 ‘여러 컴포넌트의 상위 컴포넌트에서 한 번 선언 후 Props로 전달’ 하는 방식을 사용해야 하는데요. 요청 메모이제이션을 이용하면 Store를 사용하는 것 처 이상적인 구현이 가능합니다.
요청 메모이제이션은 렌더링 주기(Render Cycle) 동안 서버 컴포넌트에서 수행되는 모든 요청을 캐시하는 React의 기능입니다.
예시와 함께 보겠습니다.
// 유저 데이터 요청 export async function fetchUserData(userId) { const response = await fetch(`https://api.example.com/users/${userId}`) return response.json() } export async function Parent({ userId }) { // 첫 번째 요청 - 해당 요청에 대해 캐시 const user = await fetchUserData(userId) return <> <h1>{user.name}</h1> <Child userId={userId} /> </> } async function Child({ userId }) { // 두 번째 요청 - 새롭게 요청하지 않고 캐시를 사용함 const user = await fetchUserData(userId) return <p>{user.name}</p> }
위 예시처럼 동일한 요청에 대해서 캐시가 되어있어 불필요한 요청이 줄어드는 장점이 있습니다.
대부분의 상황에서는 필요없겠지만, 캐시가 불필요한 경우에도 다음과 같은 방식으로 캐시를 사용하지 않을 수 있습니다.
async function fetchUserData(userId) { const { signal } = new AbortController() const res = await fetch(`https://api.example.com/users/${userId}`, { signal, }) return res.json() }
이 메모이제이션은 아래와 같은 특징을 가지고 있습니다.
- ‘서버’에 저장됨
- 단일 렌더링 주기 동안에만 유효함
- GET 메소드에만 유효함