(Graphql Apollo) dataIdFromObject와 keyFields
deprecated된 dataIdFromObject를 keyFields로 변경하면서 생긴 이슈 기록
Apollo Graphql은 Object 타입을 캐시할 때 기본 식별자로 id 필드를 사용한다. Object에 id필드가 없거나 다른 필드를 식별자로 사용하고 싶은 경우 커스텀 할 수 있다.
커스텀할 때 dataIdFromObject 또는 KeyField를 사용할 수 있는데 공식문서에서는 KeyFields 사용을 권장한다.
dataIdFromObject의 세 가지 결점
1) It's sensitive to aliasing mistakes.
2) It does nothing to protect against undefined object properties.
3) Accidentally using different key fields at different times can cause inconsistencies in the cache.
오늘 기록할 이슈는 dataIdFromObject의 2번째 결점과 관련이 있다.
keyField로 변경하기 전 다음과 같이 dataIdFromObject 코드를 사용하고 있었다.
dataIdFromObject(responseObject) {
switch (responseObject.__typename) {
case 'Book':
return `Path:${responseObject.slug}`
case 'LessonNote':
return `LessonFile:${JSON.stringify(responseObject)}`
case 'Course':
return `KDCCourseInfo:${responseObject.courseId}`
default:
return defaultDataIdFromObject(responseObject)
}
},
Book 타입은 id가 따로 없기 때문에 고유한 slug 필드가 식별자로 사용될 수 있도록 작업되어 있었고, keyField를 사용하여 다음과 같이 변경해주었다.
Book: {
keyFields: ['slug'],
},
keyFields을 바꾸니 갑자기 Book 상세 페이지 데이터를 가져오는 Query에서 에러가 발생했다.
Uncaught Invariant Violation: Missing field 'slug' while extracting keyFields from {"__typename":"BookDetail"}
같은 조건에서 바뀐건 dataIdFromObject -> keyFields 밖에 없었기 때문에 코드를 다시 되돌리니 에러는 발생하지 않았다. 그런데 쿼리 요청을 보니 상세페이지에 접근할 때 마다 캐싱된 데이터를 요청하지 않고 계속 서버에서 데이터를 받아오고 있었다.
에러 로그를 다시 확인해봤다. Book 타입이 아닌 BookDetail 타입을 가져오는 과정에서 slug를 찾지 못하고 있었다. 갑자기 BookDetail 관련 에러는 뭐지..?
BookDetail은 Book을 implement한 타입이다. 이를 통해 interface에 keyFields를 적용하면 implements의 식별자도 변경되는 것을 알 수 있었다. dataIdFromObject를 사용할 때는 typename을 'Book'으로 특정하고 있었기 때문에 slug 식별자로 변경한 코드는 BookDetail에 적용되지 않았다. keyFields를 적용한 순간 implements인 BookDetail도 식별자가 수정되었고, slug 필드를 갖고있지 않았기 때문에 에러를 발생시키고 있었다. BookDetail 타입에 slug를 추가하면 문제는 해결된다. 하지만 캐싱을 못하는건 뭐람..?
알고보니 이전 코드인 dataIdFromObject를 사용했을 때부터 있던 이슈였다. BookDetail 타입은 id를 식별자로 사용하고 있었는데 막상 필드에서는 id를 요청하고 있지 않았다. 즉, 상세 페이지의 데이터가 캐싱되지 않고 항상 Query를 재요청하고 있는 것이었다. keyField를 사용했다면 id가 없을 때 에러를 발생시켰겠지만 dataIdFromObject의 특성상 undefined인 식별자 필드를 무시해서 데이터는 캐싱되지도 않고, 캐싱된 데이터를 찾을 수도 없었다.