Apollo Client의 resetStore와 clearStore는 무슨 차이점이 있을까?

이번에는 Apollo Client의 resetStore와 clearStore의 차이점에 대해서 간단히 알아보았습니다. 두 가지 모두 cache를 삭제하는 동작은 동일한 것같습니다만, 아래의 차이점이 존재하는 듯 합니다.
2023.10.22

이번에는 Apollo Client의 resetStore와 clearStore의 차이점에 대해서 간단히 알아보았습니다.

두 가지 모두 cache를 삭제하는 동작은 동일한 것 같습니다만, 아래의 차이점이 존재하는 듯합니다.

차이점

reset store on logout의 내용을 보고 차이점을 간단히 정리해보았습니다.

Since Apollo caches all of your query results, it's important to get rid of them when the login state changes. The most straightforward way to ensure that the UI and store state reflects the current user's permissions is to call client.resetStore() after your login or logout process has completed. This will cause the store to be cleared and all active queries to be refetched. If you just want the store to be cleared and don't want to refetch active queries, use client.clearStore() instead. Another option is to reload the page, which will have a similar effect.

  • resetStore: cache를 삭제하고 active query를 refetch후 cache가 갱신됨.
  • clearStore: cache를 삭제하고 active query를 refetch하지 않음.

*active query: active query는 현재 랜더링 된 페이지에서 부르고 있는 query를 뜻하는 것 같습니다.

준비

차이점 확인을 위해 먼저 관련 환경을 만들었습니다.

먼저 React 프로젝트를 생성해 줍니다.

npm create vite@latest [project-name] -- --template react-ts

다음으로 테스트에 필요한 apollo client 를 설치해 주겠습니다.

npm install @apollo/client graphql

이후에는 간편하게 테스트하기 위해 query를 mocking 하기 위한 msw와 cache의 상태를 확인하기 위한 apollo client의 developer tools를 별도로 설치해 주었습니다.

설치 방법은 참고 자료의 관련 링크를 확인해 주세요.

코드 작성

코드는 reset store on logout의 내용을 참고로 작성했습니다.

App.tsx

...

const uriLink = createHttpLink({
  uri,
})

const client = new ApolloClient({
  link: uriLink,
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

function App() {
  return (
    <ApolloProvider client={client}>
      <ProfilePage />
    </ApolloProvider>
  )
}

ProfilePage.tsx

...
const CURRENT_USER_QUERY = gql`
  query CurrentUser {
    currentUser {
      name
      email
    }
  }
`

export function ProfilePage() {
  const { loading, error, data, client } = useQuery(CURRENT_USER_QUERY, {
    fetchPolicy: 'cache-and-network',
  })

  const handleSignOut = useCallback(async () => {
    try {
      // ↓테스트를 위해 아래의 처리를 바꿔가며 실행 해보겠습니다.
      await client.clearStore()
      // await client.resetStore()
    } catch (error) {
      console.warn('error:', error)
    }
  }, [client])

  if (loading) return <span>Loading...</span>
  if (error != null) return <span>error</span>
  return (
    <div>
      <span>{data.currentUser.name}</span>
      <span> {data.currentUser.email}</span>
      <button onClick={handleSignOut}>sign out</button>
    </div>
  )
}

차이점 확인

이후 각각 아래의 동작을 실행해 비교해 보았습니다.

  • 원하는 결과: cache만 삭제되고 query가 refetch되지 않으므로, cache만 삭제됨.
  • 결과 확인: query가 실행되지 않고 cache만 삭제됨.
  const handleSignOut = useCallback(async () => {
    try {
      await client.clearStore()
      // await client.resetStore()
    } catch (error) {
      console.warn('error:', error)
    }
  }, [client])
  • 원하는 결과: cache가 삭제되고 query가 refetch되므로, cache에 변경 없음.
  • 결과 확인: query가 한번 더 실행되며 cache에는 변경 없음.
  const handleSignOut = useCallback(async () => {
    try {
      // await client.resetStore()
      await client.resetStore()
    } catch (error) {
      console.warn('error:', error)
    }
  }, [client])

각각 상정했던 결과가 나오는 걸 확인할 수 있었습니다.

결과 확인은 아래의 developer tools를 이용하였습니다.

cache의 삭제 여부: apollo client의 developer tools

query의 Refetch여부: chrome developer tools

아쉬운점

msw을 이용해 간단한 확인을 해보았습니다만, msw의 경우에는 response의 변경에 어려움이 있어 정확한 확인은 어려울 것 같습니다.

일단 상정한 결과가 나오는 것을 확인할 수 있었지만, resetStore의 경우에는 실제로 cache가 삭제된 후 다시 cache가 갱신되었다는 정확한 확인이 어려운 점등, 좀 더 정확한 확인을 위해서는 실제 query에서 비교해 보는 게 좋을 것 같습니다.

참고자료