기본 동작 원리정규화캐시 데이터 관리한번도 쿼리가 호출되지 않은 경우, 즉 캐시가 아예 비워져 있는 경우에는 캐시데이터에 write 할 수 없다. (검증 다시 해보기)동일한 쿼리도 variables별로 따로 캐시 관리!variables가 다른데 덮어쓰이는 경우apollo client cache와 상호작용하기 (읽기, 쓰기)Query 관련 variablesFragment 관련 고려사항cache.modify로 직접 필드를 수정하는 경우cache와 broadcast 전파소스코드querylazyQueryrefetchlazyQuery 함수의 await 한 결과물을 쓰는 경우에는 broadcast 해당되지 않음standby는? (뭐지?)clearStore와 resetStore 사용 시 broadcast 차이broadcast: boolean이슈와 대안겪은 이슈대안
참고문서
기본 동작 원리

첫번째 호출 시에는 캐시에 데이터가 없다. 따라서 GraphQL Server로 실제 쿼리를 요청해서 캐시 데이터를 채운 후, 쿼리를 호출한 곳으로 결과 값을 반환한다.

두번째 호출 부터는 캐시에 데이터가 이미 있는 상태이므로, 쿼리를 요청하면 실제 서버로 요청하지 않고 캐시 데이터에 있는 내용을 그대로 반환한다.
불필요한 API 호출을 줄이고, 속도 관점에서의 이점도 가져올 수 있다. (데이터 처리 속도, 렌더링 속도 등)
정규화
링크 같이 보기
reference로 관리된다는 점을 상기 해야함.
reference로 관리되기에 객체 하나만 변경되면 다 같이 변경된 최신 데이터 상태를 유지할 수 있고,
데이터 공간등 최적화도 가능하다.
또 reference로 관리되므로, 캐시 데이터를 get 한 것에서 직접 수정하면 안된다.
캐시 데이터 관리
__typename
과 id
값이 중요하다.한번도 쿼리가 호출되지 않은 경우, 즉 캐시가 아예 비워져 있는 경우에는 캐시데이터에 write 할 수 없다. (검증 다시 해보기)
동일한 쿼리도 variables별로 따로 캐시 관리!
client.writeQuery({ query: GetLatestFirmwareDocument, data: { latestFirmware: { __typename: 'FirmwareType', deviceLineUp: { __typename: 'DeviceLineUpType', modelId: 'Nutrition Engine Pro 1-B', }, file: 'https://application.algocare.link/Firmware/DEV/NE_PRO1_REV.B_APP_V1.4.1.bin', fileSize: 27870, id: '71', majorVersion: 1, minorVersion: 6, patchVersion: 1, }, }, broadcast: false, variables: { tabletImei: '1' }, }); client.writeQuery({ query: GetLatestFirmwareDocument, data: { latestFirmware: { __typename: 'FirmwareType', deviceLineUp: { __typename: 'DeviceLineUpType', modelId: 'Nutrition Engine Pro 1-B', }, file: 'https://application.algocare.link/Firmware/DEV/NE_PRO1_REV.B_APP_V1.4.1.bin', fileSize: 27870, id: '72', majorVersion: 1, minorVersion: 7, patchVersion: 1, }, }, broadcast: false, variables: { tabletImei: '2' }, });
동일한 쿼리에 대해서도 여러개의 값들이 각각 캐시되어 관리된다.
const cacheData = client.readQuery({ query: GetLatestFirmwareDocument, variables: { tabletImei: '1' }, }); console.log('cacheData', cacheData); const cacheData2 = client.readQuery({ query: GetLatestFirmwareDocument, variables: { tabletImei: '2' }, }); console.log('cacheData2', cacheData2);

variables에 맞게 writeQuery 했던 값으로 잘 조회되는 모습이다.
variables가 다른데 덮어쓰이는 경우
이번에는 writeQuery 시
data
의 id
값을 동일하게 설정해서 테스트를 진행한다.client.writeQuery({ query: GetLatestFirmwareDocument, data: { latestFirmware: { __typename: 'FirmwareType', deviceLineUp: { __typename: 'DeviceLineUpType', modelId: 'Nutrition Engine Pro 1-B', }, file: 'https://application.algocare.link/Firmware/DEV/NE_PRO1_REV.B_APP_V1.4.1.bin', fileSize: 27870, id: '72', majorVersion: 1, minorVersion: 6, patchVersion: 1, }, }, broadcast: false, variables: { tabletImei: '1' }, }); client.writeQuery({ query: GetLatestFirmwareDocument, data: { latestFirmware: { __typename: 'FirmwareType', deviceLineUp: { __typename: 'DeviceLineUpType', modelId: 'Nutrition Engine Pro 1-B', }, file: 'https://application.algocare.link/Firmware/DEV/NE_PRO1_REV.B_APP_V1.4.1.bin', fileSize: 27870, id: '72', majorVersion: 1, minorVersion: 7, patchVersion: 1, }, }, broadcast: false, variables: { tabletImei: '2' }, });
readQuery로
variables: {tabletImei: ‘1’}
로 호출하면 minorVersion
이 몇으로 나올까?
6으로 나올 거라고 생각할 수 있으나, 실제로는 7로 조회된다.
왜냐하면 id
가 72
로 동일하기 때문에 variables: {tabletImei: ‘2’}로 writeQuery한 결과로 덮어쓰여진 상태이기 때문이다.const cacheData = client.readQuery({ query: GetLatestFirmwareDocument, variables: { tabletImei: '1' }, }); console.log('cacheData', cacheData); const cacheData2 = client.readQuery({ query: GetLatestFirmwareDocument, variables: { tabletImei: '2' }, }); console.log('cacheData2', cacheData2);

typename
과 id
가 같으니까 동일한 cacheId
라서 동일한 reference를 참조하게 된다.apollo client cache와 상호작용하기 (읽기, 쓰기)
- https://www.apollographql.com/docs/react/caching/cache-interaction/
- queries :
readQuery
,writeQuery
,updateQuery
- fragements :
readFragment
,writeFragment
,updateFragment
,useFragment
- 직접수정 :
cache.modify
Query 관련 variables
variables에 따라서도 캐시가 관리된다.
Fragment 관련 고려사항
cacheId에 fragment 상위의 객체가 이미 존재해야 한다. 거기서 fragment에 명시해둔 필드들만 추출해서 반환되는 것이기 때문이다.
cache.modify로 직접 필드를 수정하는 경우
writeQuery
, writeFragment
처럼 modify
로 캐시 필드를 수정하는 경우에도, 해당 필드를 사용하고 있는 모든 쿼리들에 전파되어 refresh가 이루어진다.마찬가지로 전파를 원치 않는 경우에는
broadcast: false
로 지정해주면 캐시 내용만 업데이트하고 전파는 되지 않는다.cache와 broadcast 전파
소스코드
inMemoryCache.js
파일의 watch
멤버변수와 broadcastWatch
멤버함수 쪽을 보면 된다.query
cache
데이터의 변화에 따른broadcast
를 별다른 조치 없이 바로 수신할 수 있다.- (선언과 동시에 호출이 진행되므로)
lazyQuery
- 쿼리를 한번 호출한 이후여야, 이후
cache
데이터의 변화에 따른broadcast
를 수신할 수 있다.
refetch
- refetch를 한 경우에도 cache를 업데이트 하므로, 전파는 이루어진다.
lazyQuery 함수의 await 한 결과물을 쓰는 경우에는 broadcast 해당되지 않음
- 함수는 명시적으로 호출하고 싶은 순간에 호출해서 결과값을 받아오는 것이므로, 전파 대상이 아님.
standby는? (뭐지?)
clearStore와 resetStore 사용 시 broadcast 차이

- clearStore는 캐시 삭제가 전파가 되지 않음.
- 전파되어 캐시를 사용하는 모든 query들의
data
가undefined
로 찍힐 거라 생각했는데, 그렇지 않고 기존의 내용을 그대로 들고 있다. - cache에선 데이터가 없어진 상태이지만, watch가 다시 실행되지 않으므로 마지막 상태에서 그냥 멈춰있는 상태라고 보면 됨.
- resetStore는 캐시 전체를 비운 후 다시 refetch까지 수행함
- refetch를 하기 때문에
undefined
가 뜨는 것부터, 호출 후 실제 데이터가 차는 것까지 수행된다.
cache를 사용하고 있는 곳에 한정해서 refetch가 수행된다.
-
network-only
, no-cache
를 쓰고 있는 쿼리는 refetch 대상에서 제외된다.
broadcast: boolean
Allow silencing broadcast for cache update methods.

broadcast: false
로 설정하여 캐시를 업데이트 할 경우, 해당 캐시를 사용하고 있는 쿼리들에도 전파가 이루어지지 않고, 따라서 로직도 다시 동작하지 않는다.이슈와 대안
겪은 이슈
- 새로운 쿼리를 반영했고, 갱신을 위한 작업을 했는데 그대로에요. 안바뀌어요
- cache-only인데 캐시가 다 날아갔어요
대안
- default fetchPolicy를
network-only
로 변경한다.
- 캐시를 사용하겠다고 마음먹은 부분에 한해 명시적으로 cache를 활용한다.
cache-only
를 사용하지 않으며, 캐시 사용을 의도한 부분도cache-first
로 정책을 사용한다.
cache.clear
는 탈퇴,로그아웃 외에는 사용하지 않는다.
위 링크처럼
cache-and-network
가 아닌 network-only
로 default 옵션을 설정할 것이다.추가로 읽을만한 것
- Configuration options
- errorPolicy ‘all’을 하면 error 객체로 에러가 오지 않고, data에 성공과 실패 모두 담아서 준다. 에러가 발생한 경우지만 onError 콜백도 호출되지 않음
- errorPolicy

- Optimistic mutation results ← 읽어야함
- optimistic UI란 먼저 서버로 요청을 보내고 응답될 것으로 예상되는 임시데이터를 사용하여 미리 UI를 업데이트 합니다. 그 후, 서버로부터 실제결과가 응답되면 다시 한번 실제 데이터로 UI를 업데이트 하는 것을 말합니다.