
우리 FISA 프론트엔드 세미나를 준비하며 캐싱에 대해 발표하기로 했다.
지금까지 개발하면서 캐싱에 대해 깊게 생각해보지 않았던 것 같아 이번 세미나를 통해서 캐싱 메커니즘에 대해 공부하면 좋을 것 같았다.
우리는 HTTP 캐싱, Next.js 캐싱 및 캐싱 전략을 준비했는데 그 중, Next.js 캐싱에 대해 살펴보고자 한다.
Next.js에는 대표적으로 총 4가지 메커니즘이 있다.

next.js는 기본적으로 캐싱을 제공한다. 위의 그림은 next.js의 공식 문서에 있는 캐싱 메커니즘의 구조도이다.
이제 각각의 메커니즘을 하나씩 살펴보자.

server component에서 수행
위의 그림 역시 공식문서에서 제공하는 Request Memization의 흐름이다. Next.js는 fetchAPI를 활용하여, 동일한 URL 및 옵션으로 발생하는 여러 옵션을 자동으로 한 번만 처리하도록 캐싱한다. 컴포넌트의 다양한 계층에서 같은 데이터를 요청하더라도 실제로 네트워크 요청은 단 한번만, 이루어지는 것이다.
위의 그림 역시 이를 표현한다. 컴포넌트의 다양한 계층에서 Request A, Request B, Request C를 여러 번 호출하지만, 실제로 호출되는 것은 Request A, B, C 딱 세번이다.
그러나, React 컴포넌트 트리의일부가 아닌 Route Handlers의 fetch 요청에는 적용이 되지 않는다.
답은 간단하다. 바로 api/router 에서 실행되는 fetch 요청은 동적인 동작을 수행하기 때문이다. 인증, 사용자 이벤트에 기반한 요청 등 오회려 memization을 하면 위험할 것이다.
동작은 다음과 같다.

같은 렌더 사이클 안에서 동일한 요청이 여러번 진행되면, Next.js가 알아서 캐싱하여 한번만 요청을 보낸 후, 다음요청부터는 실제 서버에서 값을 받아오는 것이 아닌, 캐시된 데이터로 응답을 보낸다.
이러한 memoization은 딱! 같은 렌더 사이클 안에서만 작동하기 때문에 렌더사이클이 지나면, 메모리에 저장된 캐시는 휘발된다.
그렇다면 지속적으로 사용할 수 있는 캐시는 없을까?
=> 데이터 캐시를 알아보자
Server Component에서 수행

데이터 캐시는 위에서 언급했다시피 배포 이후에도 결과가 유지되는 지속 가능한 캐시 시스템이다. 이게 가능한 이유는 Next.js가 fetch()를 확장하여 요청마다 서버에서 지속적인 캐시 방식을 사용할 수 있게 했기 때문이다.
하지만, 여기서 또 다시 의문점이 생긴다.
지속적으로 캐시를 저장한다면, 계속 캐시 히트가 날 텐데 데이트가 만약 업데이트되면 어떡하지?
그래서 우리는 Revalidation을 사용할 수 있다.
Revalidation은 말 그대로 이 데이터의 최신 여부를 확인하는 검증 과정이다.
대표적으로 시간 기반 재검증(Time-based Revalidation)과 요청 기반 재검증(On-demand Revalidation)이 존재한다.

revalidate 하는 주기를 정해주는 것이다. 위의 코드는 revalidate: 30이기 떄문에 30초 주기로 이 데이터가 신선한지 확인하는 주기를 가진다.

위의 구조도를 보자. 사용자가 캐시를 무효화하고 싶을 때, 이 On-demand Revalidation 기법을 사용한다. 일정 주기까지 기다려야 하는 Time-based Revalidation을 보완하는 셈이다.
흐름은 다음과 같다.
- 사용자가 캐시를 무효화하는
revalidationTag나revalidationPath를 넣어 서버API를 요청한다.- 캐시를 무효화한다.
- 캐시가 무효화되었기 때문에 캐시 미스가 발생하고 데이터베이스에서 신선한 데이터를 가져오게 된다.
- 캐시를 신선한 데이터로 업데이트한다.
Full-Route Cache는 한 단어로 'Snapshot'이다.

이 방식은 정적 라우트에 대해 서버 측에서 HTML과 RSC payload를 스냅샷으로 찍고, 그 스냅샷을 저장하는 방식이다. 즉, 같은 페이지를 여러 사용자가 방문하더라도, 서버는 렌더링 하지 않고 이미 캐시된 페이지(스냅샷)을 즉각적으로 사용자에게 제공할 수 있다.

이 Router Cache는 지금까지 설명한 4개의 메커니즘 중 유일하게 클라이언트 단에서 동작하는 캐싱 방식이다. 브라우저의 메모리에 RSC payload를 저장하여 사용자가 앞으로가기, 뒤로가기를 하거나 next/link를 사용하여 미리 prefetch된 라우트로 이동할 때, 새로고침 없이 빠르게 페이지 전환이 가능하다.
Full-route-Cache, Router Cache 모두 RSC 페이로드를 저장하고, 페이지를 캐싱하는 것 같은데 어떤 게 다른거지?
자료조사를 하며 정확히 이해해야 할 것 같아 구조도를 그려보았다.

가장 큰 다른 점은 다음과 같다.
- Full-Route-Cache는 서버에 캐시를 저장, Router Cache는 브라우저의 메모리에 캐시를 저장 ->
저장되는 위치가 다르다.- Router Cache는 브라우저의 메모리에 저장되기 때문에, 브라우저 탭이 유지되는 동안에만 캐시가 유지된다. 반면 Full Route Cache는 서버에 저장되어, 브라우저를 닫거나 새로 열어도
여전히 캐시된 데이터를 사용할 수 있다.- Full-Route-Cache는 HTML, RSC payload, fetch 결과를 저장하는 반면, Router-Cache는 RSC payload, react의 상태가 저장된다. 즉, FUll-Route-Cache는 페이지 자체를
스냅샷찍듯이 저장하고, Router Cache는페이지 전환 데이터(클라이언트 사이드 렌더링에 필요한 데이터)등을 저장한다고 할 수 있겠다.
지금까지 Next.js의 4가지 캐싱 메커니즘에 대해 알아보았다.
마지막으로 이 4가지 캐싱 메커니즘을 공부하며 가장 헷갈린 것들을 다시금 되돌아보면 다음과 같다.
요청 스코프 메모리에 저장요청 스코프 메모리란?
하나의 HTTP 요청이 서버에 들어와 처리되는그 순간 동안만 유지되는 메모리 공간
"경로 단위"로 정적 페이지를 저장하고 배포하기 위함경로 단위가 아닌, 데이터 요청 단위의 캐시가 필요하다. 구조상 파일보다는 메모리 기반 내부 캐시 계층이 적합1) 브라우저에서 페이지 접근
2) 클라이언트는 Router Cache를 사용한다. (캐시 히트 시 즉시 렌더링)
3) 캐시 미스일 경우 서버 요청
4) 서버에서 FUll Route Cache를 확인한다. (캐시 히트 시 응답 반환)
5) 캐시 미스일 경우 서버사이드 렌더링 진행)
6) 페이지가 구성되면서 내부 fetch가 호출될 경우
- Request Memoization 사용
- Memoization이 없을 경우 Data Cache 확인
- Data Cache가 또 미스일 경우, 실제 네트워크 fetch 수행 및 캐시 업데이트
7) 렌더링 완료 및 Full Route Cache에 저장(정적 페이지일 경우)
8) 응답이 반환되면서 클라이언트 Router Cache에도 저장된다.
---출처 : Next.js 공식문서
지금까지 Next.js의 캐싱 메커니즘에 대해 알아보았다. 그럼 20000~ 😊😊
좋은 글 읽고 갑니다 ^^