Next.js default caching 변경(force cache -> no cache)
TMT프론트엔드 성능을 최적화하는 것은 어려운 일이며, 특히 클라이언트와 서버 간의 워터폴 요청이 주된 문제입니다. Next.js App Router를 도입할 때 이 문제를 해결하기 위해, React Server Components를 사용해 클라이언트-서버 간의 단일 요청으로 처리하는 방식을 고려했습니다. 이를 통해 서버가 동적으로 작동하게 되었고, JAMstack의 초기 로딩 성능이 저하될 가능성이 있었습니다. 이를 해결하기 위해 부분적인 사전 렌더링을 도입해 양쪽의 장점을 모두 살리는 방법을 개발했습니다.
하지만, 개발 경험은 캐싱 기본값과 제어 방식으로 인해 악화되었습니다. fetch()의 기본 캐싱 방식은 성능을 중시하여 캐싱을 우선시했지만, 빠른 프로토타입 제작이나 동적인 애플리케이션에서는 불편함을 초래했습니다. 또한 fetch()를 사용하지 않는 로컬 데이터베이스 접근 제어에 대한 제어권이 부족했고, unstable_cache()는 비직관적이었습니다. 이에 따라, dynamic, runtime, fetchCache, dynamicParams, revalidate 등의 세그먼트 수준 설정이 필요해졌습니다.
이제 우리는 이 복잡함을 단순화하는 새로운 실험적 모드를 개발했습니다. 이 모드는 <Suspense>와 use cache라는 두 가지 개념만을 바탕으로 작동합니다.
동적 모드(Dynamic)
대시보드처럼 매우 동적인 앱을 개발 중이라면, <Suspense> 경계를 사용하여 동적 데이터 가져오기와 스트리밍을 구현할 수 있습니다. 이를 통해 애플리케이션의 껍데기는 즉시 로드되며, 동적인 데이터는 비동기적으로 처리됩니다.
정적 모드(Static)
정적인 페이지를 만들 때는 use cache를 사용하여 페이지 전체를 캐시하도록 지정할 수 있습니다. 이렇게 하면 데이터를 가져오는 모든 것이 캐시되며, 페이지는 정적으로 렌더링됩니다.
부분 캐싱(Partial Caching)
루트 레이아웃에 use cache를 사용하여 레이아웃은 캐시되도록 설정하고, 특정 페이지는 동적으로 작동하도록 혼합하여 사용할 수 있습니다.
또한, 캐시된 함수와 캐시 태그 지정 기능을 통해 API 호출 수준에서 캐시를 추가하고, 캐시의 수명을 설정할 수 있습니다. 예를 들어, 특정 페이지나 데이터 항목이 얼마나 오래 캐시에 머물러야 하는지 cacheLife() API를 통해 지정할 수 있습니다.
실험적 모드
이 기능은 여전히 실험 중이며, 아직 프로덕션에 사용할 준비가 되지 않았습니다. 오류 처리 스택도 개선해야 할 부분이 남아 있지만, 새로운 방식이 잘 작동한다면 추후 선택적 버전으로 도입할 예정입니다.
Next.js의 카나리 버전에서 이 실험적 기능을 사용해볼 수 있으며, next.config.ts에서 dynamicIO 플래그를 활성화해야 합니다.