Essay/기술 회고

[Frontend] Apollo 캐시값 갱신 - Troubleshooting 기록

개발자 이우진 2022. 1. 14. 21:39

문제점

header의 설정값 (global한 값)을 바꾼 뒤 다른 페이지로 이동하면 바꾼 값이 적용되지 않는 현상이 발생했다

원인

header의 설정값은 apollo useQuery를 이용해 불러오고 있었는데, refetch로 변경한 뒤 다시 불러올 때는 예전 캐시 값을 읽어와서 값이 변경되지 않았다.

배경

header에서 사용하고 있던 api 쿼리문은 variable을 전달하지 않으면 가장 마지막에 읽었던 값을, variable을 주면 해당 값을 반환하는 api였다.

  • () => 마지막에 읽었던 값
  • (variable) => variable에 대응되는 값

이런 상황에서 사용자가 값을 변경하면 두 번째 방법으로 호출하고, 초기에 fetch할 때는 첫 번째 방법으로 호출하여 마지막에 읽었던 값이 반환되기를 기대했다. 그러나 실제로는 cache-first 정책으로 동작하고 있었기 때문에 다음과 같이 동작하고 있었다

  1. myQuery() => data-1 [마지막 읽은 값]
  2. 값 변경: myQuery(3) => data-3
  3. 페이지 이동
  4. myQuery() => data-3 가 나오기를 기대
    • 그러나 캐시에 저장된 데이터가 불러와져 실제 반환값은 data-1

해결법

myQuery(variable)을 수행한 뒤에 myQuery()에 해당하는 캐시 값을 수행한 쿼리의 결과로 업데이트하였다. 코드로는 client.writeQuery()를 사용하였다.

    const client = useApolloClient();
  const updateDefaultMyQueryCache = (dataToWrite: unknown) => {
    client.writeQuery({
      query: MY_QUERY,
      data: dataToWrite,
    });
  };

  const { loading, error, data, refetch } = useQuery<
    DataType,
    VariablesType
  >(MY_QUERY, {
    variables: { ... },
    onCompleted: (responseData) => {
      updateDefaultMyQueryCache(responseData);
    },
  });

장점

myQuery(variable) 수행 후 myQuery()를 refetch하여 쿼리를 업데이트 하는 방법도 있으나, 이 경우에는 한번 네트워크 요청을 한 뒤에 네트워크 요청을 한번 더 하게 된다. 대신 이 경우에는 myQuery()의 결과가 어떻게 바뀔 지 명확하고 정확도가 중요한 데이터도 아니어서 캐시를 업데이트 하면 비싼 네트워크 요청을 줄일 수 있다.
재미있는 점은 myQuery()의 캐시 업데이트가 myQuery의 상위 집합을 요청하는 쿼리의 캐시에도 영향을 준다는 것이다. 그쪽 캐시도 수정해야 하는지 확인해 봤는데, 같은 데이터를 불러올 때 random access를 apollo가 잘 해줘서 그런지 알아서 업데이트된 값으로 반환해주고 있었다.

개선점

캐싱이 적용이 안 된 부분이 있다면 더 확대해 적용하고 싶다. (로딩이 눈에 보이는 페이지가 있어서 거기에 적용이 안 된줄 알았는데 의외로 거기는 되어 있었다) 특히 mutation 후에 refetch로 쿼리를 업데이트하는 부분이 몇 군데 있는데, 이 부분 중 몇군데는 캐시 업데이트로 바꿔도 될 지 고민해 봐야겠다. 그렇게 하면 네트워크 요청과 로딩 시간을 줄일 수 있을 것이다.

 

출처
Apollo Docs - Reading and writing data to the cache