Loki's Blog

Apollo Client의 fetchPolicy 기본값이 cache-first인 이유

  • CS
  • GraphQL
  • Apollo Client
2026. 08. 08.

들어가며


최신 데이터가 좋다는 생각이, 네트워크 비용을 간과했다.

Apollo Client를 쓰면 fetchPolicy라는 옵션을 만나게 된다. 캐시를 얼마나 신뢰하고, 네트워크에는 언제 다시 요청할지를 정하는 설정이다. 처음 이 옵션을 마주했을 때는 “캐시도 보여주고 네트워크로 최신 데이터도 확인”하는 cache-and-network가 가장 효율적으로 보였다. 캐시 덕분에 화면은 바로 뜨고, 그 사이 네트워크로 최신 데이터까지 확인하니 사용자 입장에서 오래된 값을 볼 일이 없었다. 웬만하면 이 정책을 기본으로 쓰면 된다고 여겼다.

하지만 라이브러리가 기본값으로 설정한 정책은 “캐시 값이 있으면 네트워크 요청 없이 그대로 쓰는” cache-first였다. 최신 데이터를 보여주는 쪽이 항상 나아 보이는데, 라이브러리는 왜 오래된 값을 보여줄 수도 있는 정책을 기본으로 삼았을까?

다섯 가지 정책이 하는 일


각 정책이 데이터와 맺는 약속은 서로 다르다.

Apollo Client는 캐시와 네트워크를 조합하는 방식을 몇 가지 정책으로 나눈다. 대표적으로 다음 다섯 가지가 있다.

정책캐시 값이 있으면네트워크 요청어울리는 상황
cache-first캐시 값을 바로 반환캐시가 없을 때만 보냄약관, 설정값처럼 자주 바뀌지 않는 데이터
cache-and-network캐시 값을 먼저 보여줌캐시 유무와 관계없이 보냄최신 변경을 빠르게 반영해야 하는 데이터
network-only캐시 값을 사용하지 않음항상 보낸다. 응답 전까지 로딩 상태가 될 수 있음재고, 잔액처럼 서버의 최신 판단이 필요한 데이터
no-cache캐시 값을 사용하지 않고 응답도 저장하지 않음항상 보냄캐시에 남기지 않아야 하는 민감한 데이터
cache-only캐시 값만 반환절대 보내지 않음다른 경로에서 캐시가 이미 채워졌다고 확신하는 데이터

표에서 cache-first 줄만 다시 보면, 캐시 값이 있는 순간 쿼리는 끝나고 네트워크 요청은 나가지 않는다. 다른 사용자나 다른 화면이 그사이 데이터를 바꿔도 이 화면은 알 방법이 없다. 그런데도 이 정책이 기본값이라는 건, 라이브러리가 최신 상태 유지보다 불필요한 네트워크 요청을 줄이는 것을 기본 안전장치로 택했다는 뜻이다.

최신 데이터에도 비용이 든다


cache-and-network는 네트워크 요청을 포기하지 않는다.

cache-and-network는 캐시 값을 먼저 보여주고, 그 뒤에도 매번 네트워크 요청을 보내 최신 데이터로 갱신한다. 방금 저장한 값이 캐시에 있어도 예외는 없다.

다른 사용자의 변경을 빠르게 반영해야 하거나 화면에 들어올 때마다 최신 상태를 확인해야 한다면, 이 반복 요청은 정확히 원하는 동작이다. 반대로 거의 바뀌지 않는 데이터라면 문제가 된다. 배포 후 몇 주째 그대로인 카테고리 목록에 cache-and-network를 걸어두면, 캐시 덕분에 화면은 즉시 표시되지만 사용자가 페이지를 열 때마다 이미 가진 값과 거의 같은 응답을 받으려 서버에 요청이 나간다.

Notion image
같은 화면, 같은 행동인데도 정책 하나로 서버가 받는 요청 수가 갈린다.

같은 화면에서 같은 사용자가 같은 행동을 하더라도 fetchPolicy에 따라 서버가 받는 요청 수는 달라진다. 트래픽이 적을 때는 이 차이가 잘 안 보이지만, 사용자가 늘거나 자주 방문되는 진입점이 되면 누적된 비용으로 드러난다. 정확한 비용은 화면별 트래픽을 직접 측정해야 알 수 있다. 다만 데이터가 거의 바뀌지 않는다는 사실만으로도, 매번 네트워크 요청을 보내는 게 불필요할 수 있다는 판단은 가능하다.

판단은 정보를 가진 쪽에 있다


라이브러리는 이 쿼리가 얼마나 자주, 누구에 의해 바뀌는지 알 수 없다.

어떤 화면에서 쓰이는지, 데이터를 누가 바꾸는지, 사용자가 얼마나 빨리 최신 상태를 확인해야 하는지는 쿼리를 작성하는 개발자만 안다. 라이브러리는 이런 정보 없이 모든 쿼리를 마주한다. 이 정보 비대칭 속에서 네트워크 요청을 기본으로 걸면, 거의 안 바뀌는 화면에도 불필요한 요청이 쌓인다.

정보가 없는 쪽이 기본값을 정할 때는, 되돌리기 쉬운 선택이 안전하다.

반대로 cache-first를 기본으로 두면, 최신 데이터가 필요한 화면의 개발자가 그 필요성을 확인한 뒤 직접 정책을 바꾸면 된다. 판단은 정보를 가진 쪽에 남겨두고, 라이브러리는 보수적인 값만 깔아두는 셈이다. 공식 문서가 cache-first를 요청 수 최소화 정책으로 설명하는 이유도 비슷할 것이다. 대부분의 데이터는 매 순간 바뀌지 않으니, 최신을 보장하겠다는 약속보다 불필요한 요청을 줄이겠다는 약속을 기본값으로 삼은 것이다.

정작 나는 기준 없이 선택했다


라이브러리는 판단 기준이 있었지만, 나의 판단 기준은 없었다.

fetchPolicy를 처음 쓸 때부터 cache-and-network가 안전하다는 생각은 꽤 오래갔다. cache-firstcache-and-network 중 하나를 고를 때마다, 최신 데이터를 보여주는 쪽이 낫다고 여겼다. 돌아보면 그 판단은 나 혼자 화면을 오가며 테스트하는, 한 명의 사용자 입장에서만 두고 내린 것이었다. 실제 사용자가 늘어나면 그만큼 네트워크 요청도 함께 누적된다는 사실은 고려하지 않았다.

이 데이터는 매번 네트워크 요청이 필요한가? 나는 이 질문에 답한 적이 없다. 새 쿼리를 쓸 때마다 변경 주기나 최신성 요구사항을 따지기보다, 비슷한 화면이 어떤 정책을 쓰는지부터 봤다. 근처 화면이 cache-and-network였다면, 그 선택이 맞는지 되물어볼 생각도 없이 그대로 가져왔다. 직접 고민하기보다 다른 화면을 만든 사람의 결정을 따라간 셈이다.

Notion image

cache-and-network가 잘못된 정책은 아니다. 캐시 값을 먼저 보여주면서 최신 데이터도 확인할 수 있어, 적절한 상황에서는 좋은 선택이다. 다만 무난해 보인다는 이유만으로 모든 데이터에 적용하면 필요한 수준보다 많은 요청을 보내게 된다. 화면은 정상 동작하고 캐시 덕분에 느려 보이지도 않지만, 트래픽이 많은 화면일수록 이 불필요한 요청이 그대로 서버 부하로 쌓일 수 있다.

정책을 고르는 기준은 데이터의 성격이다


이 데이터가 왜 바뀌고 얼마나 자주 바뀌는지부터 확인해야 한다.

같은 화면 안에서도 데이터마다 성격은 다르다. 게시글 목록은 다른 사용자가 글을 쓰면 바뀌지만, 상단 카테고리 목록은 배포 전까지 거의 안 바뀐다. 한 화면에 있다고 같은 fetchPolicy를 써야 하는 건 아니다.

다음 두 질문에 대한 답을 조합하면 적절한 정책을 어느 정도 좁힐 수 있다.

  1. 다른 사용자나 다른 화면이 이 데이터를 변경할 수 있는가?
  2. 변경이 발생했을 때 현재 화면이 즉시 그 사실을 알아야 하는가?
Notion image

정리하며


같은 불확실함 앞에서, 라이브러리와 나는 다른 선택을 했다.

라이브러리는 이 쿼리에 정말 매번 네트워크 요청이 필요한지 알 방법이 없었고, 그 불확실성 앞에서 비용이 적은 쪽, cache-first를 택했다. 나는 같은 불확실성 앞에서 다른 선택을 했다. 그 답을 알 수 있는 사람은 라이브러리가 아니라 나였는데도, 그 사실조차 의식하지 않은 채 습관을 정답 삼아 판단을 건너뛰었다. 비용은 보지 못하고 최신 데이터가 좋다는 사실만 생각했다.

Notion image
요청 비용을 치를 값어치가 있는가?

이제 새 쿼리에 fetchPolicy를 설정할 때는 이 데이터가 누구에 의해 얼마나 자주 바뀌는지, 변경이 생겼을 때 사용자가 얼마나 빨리 알아야 하는지부터 확인한다. 그 답이 "거의 안 바뀐다"거나 "다음 진입 때 확인해도 된다"면 cache-first로 충분하고, 화면에 들어올 때마다 다른 사용자의 변경을 확인해야 한다면 cache-and-network가 필요하다. 결국 fetchPolicy를 고르는 기준은 최신 데이터가 좋은가가 아니라, 이 데이터의 최신성이 네트워크 요청 비용을 감수할 만큼 중요한가다.

관련 글