export default function Page() {
return (
<h1>페이지 캐싱 실습하기 🤓</h1>
);
}
npm run build
명령어를 통해 프로젝트를 빌드해보면 위와 같은 오류를 발견할 수 있다.
useSearchParams()가 Suspense
경계로 감싸져야 한다는 뜻!
이는 검색바 컴포넌트인 📁searchbar.tsx 파일은 정적으로 페이지를 생성하는데
useSearchParams()
훅을 호출하고 있기 때문에 발생한 오류이다.
useSearchParams()
훅은 주석으로 작성해놓은 것처럼 현재 페이지에 전달된 쿼리 스트링을 꺼내오는 역할을 한다. 따라서 빌드 타임에는 쿼리 스트링의 값을 절대 알 수가 없기 때문에,빌드 타임에 오류가 발생하게 되는 것이다.
이 오류를 해결하기 위해서는 해당 컴포넌트를 사전 렌더링 과정에서 완전히 배제시키면 되는데!
📁searchbar.tsx 컴포넌트가 클라이언트 측에서만 실행될 수 있도록 위와 같이 리액트의
Suspense
객체로 감싸준다.
또한 fallback 옵션을 통해 대체 UI를 설정해준다.
이렇게 설정하면 Searchbar 컴포넌트는 사전 렌더링 과정에서 아예 배제되며, 컴포넌트의 비동기 작업 종료 시까지 fallback 옵션으로 설정한 대체 UI를 띄운다.
비동기 작업은 Searchbar 컴포넌트에서 사용한 useSearchParams() 훅에서 진행되는데, 사용자가 검색어를 입력해서 넘기면 쿼리스트링으로 저장되기 때문에 이 전까지 대체 UI가 보여지게 되는 것이다.
프로젝트를 빌드해보면 인덱스 페이지도 Dynamic Page로 설정되어 있어 풀 라우트 캐시가 적용되지 않는 것을 확인할 수 있다.
따라 인덱스 페이지에 해당하는 페이지들을 모두 Static Page로 바꿔주어 풀 라우트 캐시가 적용되도록 해보자.
async function Footer() {
// cache 옵션을 "force-cache"로 설정함으로써 Dynamic Page -> Static Page로 변경.
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_SERVER_URL}/book`,
{ cache: "force-cache" }
);
if (!response.ok) {
return <footer>제작 @Stopsoo</footer>;
}
const books: BookData[] = await response.json();
const bookCount = books.length;
return (
<footer>
<div>제작 @Stopsoo</div>
<div>{bookCount}개의 도서가 등록되어 있습니다.</div>
</footer>
);
}
컴포넌트에서 api를 불러올 때 cache 옵션을 설정하지 않으면 기본값인 "no-store"
로 설정되며, 이는 해당 컴포넌트를 포함하는 페이지를 동적 페이지로 만든다.
따라서 위와 같이 cache 옵션을 "force-cache"
로 변경하여 정적 페이지가 되게 하였다.
이런 식으로 페이지들마다 적용해주면 최상단(
/
)에 인덱스 페이지가 빈 동그라미로 변경되며 정적 페이지로 표시된 것을 확인할 수 있다.