GraphQL 클라이언트에서 데이터 캐싱은 성능 향상과 네트워크 요청 최소화를 위해 매우 중요합니다.
Apollo Client는 강력한 캐싱 메커니즘을 제공하며, 이를 통해 서버와의 상호작용을 더욱 효율적으로 관리할 수 있습니다.
이번 포스트에서는 `InMemoryCache`를 사용하여 캐싱을 설정하는 방법을 설명하겠습니다.
캐시 설정 코드
다음은 InMemoryCache를 설정하는 코드입니다.
여기서 주요 포인트는 `typePolicies`를 사용하여 특정 쿼리와 필드의 캐싱 동작을 정의하는 것입니다.
import { InMemoryCache } from '@apollo/client';
import { ALL } from '../../utils/constants';
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
itemsPageCount: {
keyArgs: false, // __ref값을 임의로 설정
},
items: {
keyArgs: ['_id'],
merge(existing = [], incoming = [], { args, readField }) {
const newArr = [...existing, ...incoming];
const uniqueArr = newArr.filter(
(arr, index, callback) =>
index === callback.findIndex((t) => t.__ref === arr.__ref),
);
let resultArr = [];
if (args.itemCategoryId !== ALL) {
resultArr = uniqueArr.filter(
(_id) =>
readField('itemCategoryId', _id) === args.itemCategoryId,
);
} else {
resultArr = uniqueArr;
}
return resultArr;
},
},
},
},
},
});
typePolicies 설정
itemsPageCount 필드
- keyArgs: false: keyArgs는 필드의 캐시 키를 결정하는 인자를 설정합니다.
false로 설정하면 모든 인자를 무시하고 동일한 캐시 키를 사용합니다.
즉, itemsPageCount 쿼리의 모든 요청은 동일한 캐시 항목을 공유합니다.
items 필드
- keyArgs: ['_id']: _id 필드를 캐시 키의 인자로 사용합니다. _id가 동일한 항목들은 동일한 캐시 키를 공유하게 됩니다.
- merge 함수: existing(기존 데이터)과 incoming(새로 들어온 데이터)을 병합하는 역할을 합니다.
데이터 병합 및 중복 제거
merge 함수 내에서 기존 데이터와 새로운 데이터를 병합하고 중복을 제거하는 과정은 다음과 같습니다.
1. 데이터 병합
const newArr = [...existing, ...incoming];
2. 중복 제거
const uniqueArr = newArr.filter(
(arr, index, callback) =>
index === callback.findIndex((t) => t.__ref === arr.__ref),
);
cache는 __ref 라는 값으로 데이터 중복을 방지할 수 있습니다.
여기서 __ref는 Apollo Client의 캐시에서 객체를 고유하게 식별하는 참조 값 입니다.
__ref는 객체의 타입과 ID를 결합한 문자열로 구성됩니다. 예를 들어, Item:1과 같이 객체의 타입과 ID를 나타냅니다.
지금까지 __ref에 관한 내용을 정리해보면 이렇습니다.
- 고유 참조 생성: Apollo Client는 각 객체에 대해 __ref를 생성하여 이를 고유하게 식별합니다.
- 중복 제거 로직: 배열 내에서 __ref가 동일한 객체들을 제거하여 중복을 방지합니다.
3. 카테고리 필터링
let resultArr = [];
if (args.itemCategoryId !== ALL) {
resultArr = uniqueArr.filter(
(_id) =>
readField('itemCategoryId', _id) === args.itemCategoryId,
);
} else {
resultArr = uniqueArr;
}
리펙토링
위의 데이터 병합 및 중복 제거 파트에서 3번 카테고리 필터링 부분의 코드를 리펙토링 시켜보겠습니다.
// store 코드 부분
function setItemPage() {
const initValues = {
pageNumber: 1,
}
const { subscribe, set, update } = writable({ ...initValues })
const nextPage = () => {
update((data) => {
data.pageNumber = data.pageNumber + 1
itemPageLock.set(true) // 페이지 넘버가 증가하면 서버에 새로운 데이터를 호출하게되기 때문에 true를 줘서 Lock을걸어줌
itemPageLoading.set(true)
return data
})
}
export const itemPage = setItemPage()
// cache설정코드
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
itemsPageCount: {
keyArgs: false,
},
items: {
keyArgs: ['_id'],
merge(existing = [], incoming = [], { args, readField }) {
const newArr = [...existing, ...incoming]
// 중복 제거
const uniqueArr = newArr.filter(
(arr, index, callback) =>
index === callback.findIndex((t) => t.__ref === arr.__ref),
)
// categoryId 필터링
let resultArr = []
const itemPageNumber = get(itemPage)
if (itemPageNumber.pageNumber <= 1) {
resultArr = incoming
} else {
resultArr = newArr
}
return resultArr
},
},
},
},
},
})
const client = new ApolloClient({
link: from([authLink, link]),
cache,
})
전역으로 설정해둔 store에서 값을 가져와 페이지 번호를 조건문으로 제시하여,
새로운 값을 가져올지, 기존의 값과 새로운값을 더한 값을 가져올지 리펙토링 해주었습니다.
결론
이렇게 설정된 InMemoryCache는 쿼리 결과를 효율적으로 캐싱하고, 필요한 경우 병합과 중복 제거 및 필터링을 수행하여 최적의 데이터를 제공합니다.
이를 통해 클라이언트 애플리케이션의 성능을 극대화할 수 있습니다.
'개인 공부 > graphql' 카테고리의 다른 글
cache.modify 와 cache.writeQuery의 차이점과 활용법 (0) | 2024.07.29 |
---|---|
Graphql문 효율성 증가 - fragment (0) | 2024.07.28 |
그래프큐엘 타입정의 (6) | 2024.07.25 |
그래프큐엘 사용하는 이유 (0) | 2024.07.25 |