[WEB] 캐싱 전략 세우기

Jay ·2024년 1월 5일
0

1. 서론

SWR을 사용하며, 라이브러리에서 관리되는 캐시와 HTTP 캐시가 충돌을 일으켜 예기치 못한 작동을 하는 경우가 발생하였다.

따라서 이번 기회에, 웹 서비스에서 사용되는 캐시들(HTTP CACHE)에 대해 좀 더 깊게 이해하여 보고자 이들을 개괄적으로 정리해보고 서비스에 어떻게 캐싱을 적용할 지 탐구해보기로 하였다.

2. 주요 캐싱 레이어

2.1 Web Cache(HTTP cache)

웹에서 정보를 저장하고 재사용하는 기술.

웹 페이지나 파일을 불러올 때 속도와 효율을 높이기 위해 고안되었다. 웹 브라우저나 서버에서 사용될 수 있으며, 한 번 다운로드된 자원(이미지, 페이지, 스크립트 등)을 로컬에 저장하여, 필요할 때에 빠르게 제공하여 사용할 수 있게 한다..

rfc9111 명세에 따르면 HTTP 캐시는 크게 Private, 그리고 shared 캐시로 구분된다.

1) Private cache

클라이언트에서 사용되는 캐시, 주로 브라우저 캐시를 지칭한다. 유저들간에 공유되지 않고 개인적인 응답을 저장하는 용도로 사용된다.

  • 브라우저 캐시
    브라우저 단에서 저장되어 사용되는 캐시. 브라우저 별로 그 구현이 상이하다.

2) Shared cache

클라이언트와 서버간에 위치하며, 유저들에게 서버응답을 유저들에게 제공되는 캐시. proxy 캐시와 managed 캐시로 구분된다.

managed cache

서비스 개발자들이 오리진 서버의 부담을 완화하기 위해 배포한 캐시.
리버스 프록시, CDN 등이 이에 해당한다.

  • CDN cache
    Content Delivery Network 혹은 Content Distribution Network.
    사용자가 매번 물리적으로 멀리 떨어져있는 origin 서버에 데이터를 요청하고 받는 데는 많은 응답 시간이 필요하다.
    이를 위해서 클라우드플레어와 같은 CDN 서비스들은 전 세계 여러 지역에 프록시 서버를 마련하여 사용자와 물리적으로 가까운 곳에 필요한 데이터를 저장하여 제공한다.
Proxy cache

네트워크 프록시 서버단에서 사용되는 캐시.

휴리스틱 캐시 ?
HTTP는 기본적으로 가능한 많은 데이터를 캐싱하는 것을 목표로 한다. 따라서 브라우저는 명시적인 Cache-control이 없더라도 자체적인 휴리스틱 알고리즘을 통해 리소스를 캐싱한다.

Cache-control이 널리 도입되기전에 브라우저 단에서 사용되던 workaround로, 기본적으로 cache-control 헤더를 사용하여 명시적으로 캐싱을 설정하는 것이 권장된다.

2.2 어플리케이션 캐시

React-query 및 SWR같은 라이브러리들이 이에 해당한다.
HTTP 캐시의 Stale-while-revalidate 방법에 영향을 받아 고안되었으며, 캐싱이 HTTP단이 아닌 클라이언트의 메모리 단에서 일어난다.

3. Stale-while-revalidate ?

stale-while-revalidate는 개발자가 캐싱된 콘텐츠를 즉시 로드하는 즉시성과 갱신된 최신 콘텐츠가 향후에 사용되도록 보장하는 최신성 간의 균형을 유지하는데 도움을 주는 HTTP Cache-Control 확장 디렉티브이다.

max-age 프로퍼티와 함께 cach-control 응답 헤더에 사용한다.

요약하면, 유저에게 캐시된 응답(Stale)한 데이터를 제공하고, 그동안(While) 백그라운드에서 다시 데이터를 revalidate(최신화)하는 전략이다.

Tanstack-query 및 SWR이 이에 영향을 받은 라이브러리이다.

4. Next.js의 extended fetch API

그렇다면 Next.js의 fetch API는 어떻게 작동하는 것일까?

서버 컴포넌트가 도입된 next.js의 App router에서 fetch는 어느 단계에서 데이터를 캐싱하는지 궁금해졌다.

Data Cache

Next.js는 서버 단에 'Data Cache'라는 별도의 캐시 레이어가 존재하고, 이곳에 fetch의 결과물들을 저장하여 어플리케이션 내에서 재사용하고 있다고 한다.

  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })

다음과 같이 cache 옵션을 설정하면, 클라이언트 단에서는 앞서 알아보았던 HTTP cache를 설정하는 cache-control로 작동하고, 서버 측에서는 Data Cache 레이어의 캐시가 설정되는 것이다.

5. 결론

여러가지 캐싱 레이어들과 사용 방법들에 간단하게 알아보았다.

종합하여 결론내려보자면,

작은 규모의 어플리케이션 수준이라면 단순히 Tanstack query나 SWR같은 라이브러리만을 사용하고, HTTP cache는 no-store 혹은 no-cache header를 적용하여, 캐쉬저장소를 일원화 하여 생산성 향상을 추구하는 것도 좋은 선택지가 될 수 있을 것이다.

하지만 어플리케이션의 규모가 커지고, 서버 트래픽이나 유저의 사용자 경험까지 고려한다면, 네트워크 단에서 캐싱을 하는 것은 선택지가 아니라 필수가 될 것이다.

따라서, 더 좋은 프로덕트를 목표로 한다면 다양한 캐싱 레이어들을 상황에 따라 적절하게 모두 이용할 수 있어야 할 것이다.

profile
Jay입니다.

0개의 댓글