GQL의 Client caching 관련

query { stories { id, text, likeCount } } // 1번

query { story(id: "123") { id, text, likeCount } } // 2번

데이터 정규화

query AllStories { 
  stories { 
    id, 
    text, 
    likeCount 
  } 
}

query SingleStory { 
  story(id: "123") { 
    id, 
    text, 
    likeCount 
  } 
}

위의 두 쿼리의 결과는 아래와 같이 정규화 되어 저장된다.

{
  "ROOT_QUERY": {
    "stories": [
      { "__ref": "Story:123" },
      { "__ref": "Story:456" },
      // ...
    ],
    "story({"id":"123"})": { "__ref": "Story:123" }
  },
  "Story:123": {
    "id": "123",
    "text": "Hello World",
    "likeCount": 10
  },
  "Story:456": {
    "id": "456",
    "text": "Another story",
    "likeCount": 5
  }
  // ...
}
  1. 데이터 정합성을 지킨다.
    • Story:123이라는 데이터는 두 개의 쿼리에서 사용되지만, 데이터는 한 곳에만 저장되므로 정합성을 유지한다.
  2. 효율적인 캐시 업데이트
    • 예를들어 캐시가 업데이트 되어 Story:123결과가 업데이트 되어야한다면, Story:123을 사용하는 모든 쿼리의 캐시가 아닌, 하나의 필드만 업데이트하면 된다.
  3. 참조를 통한 중복 제거 (메모리 효율)
    • 동일 객체의 여러 복사본을 저장하지 않아도 되어 메모리 사용이 효율적임

주의해야할 케이스 🧐 - 부분객체업데이트

예를들어 아래의 초기 쿼리 결과는 두번째와 같은 결과로 캐시에 데이터가 저장된다.

query GetUserProfile {
  user(id: "123") {
    id
    name
    email
    age
    bio
  }
}
{
  "User:123": {
    "id": "123",
    "name": "Moonhee Kim",
    "email": "Mooneee@example.com",
    "age": 30,
    "bio": "Software developer"
  }
}

그런데 만약, 사용자가 자신의 '이름'만 변경하는 뮤테이션을 실행한다고 했을 때 해당 뮤테이션의 결과는 아래와 같다.

mutation UpdateUserName {
  updateUser(id: "123", name: "Boogie") {
    id
    name
  }
}
{
  "data": {
    "updateUser": {
      "id": "123",
      "name": "Boogie"
    }
  }
}

이 시점에서 클라이언트는 캐시 업데이트를 주의깊게 해야한다. 객체 부분을 업데이트하는게 아니라 전체 업데이트 해버릴 수 있기 때문.

{
  "User:123": {
    "id": "123",
    "name": "Boogie"
    // email, age, bio가 사라짐!
  }
}

이러한 동작의 해결방븝으로 Apollo Client 는 기본 병합 동작을 제공한다.
즉, 기본으로 반환된 필드만 업데이트하고 나머지만 유지하는 것.

만약 뮤테이션 결과를 수동으로 캐시에 적용할 때는 아래와 같이 updateQuery를 사용 하면 된다.

client.mutate({
  mutation: UPDATE_USER_NAME,
  variables: { id: "123", name: ""," },
  update: (cache, { data: { updateUser } }) => {
    const existingUser = cache.readQuery({ query: GET_USER_PROFILE, variables: { id: "123" } });
    cache.writeQuery({
      query: GET_USER_PROFILE,
      data: { user: { ...existingUser.user, ...updateUser } },
    });
  },
});

그냥 캐시에 넣지 않고 refetch 하는 방법도 물론 있긴 하겠다.

Apollo Client 의 UpdateQuery vs WriteFragment