이번에는 useQuery와 useMutation의 캐시에 대해 다뤄보려고 한다.
먼저 세상 너무나도 간단한 useQuery에 대해 알아보자
export const FETCH_POSTS_QUERY = gql`
query getPosts {
getPosts {
id
body
createdAt
username
...
}
}
`
위의 쿼리에 대해 클라이언트 부분에서 React의 useQuery hook을 이용하여 불러오도록 해보자.
const { data, loading, error } = useQuery(FETCH_POST_QUERY)
네, 끝입니다 (간단하쥬?)
이렇게 간단하게 불러올 수 있고 혹시나 특정 id에 해당하는 데이터 불러와야 하는 경우
export const FETCH_POST_QUERY = gql`
query getPost($postId: ID!) {
getPost(postId: $postId) {
id
body
createdAt
username
...
}
}
`
const { data, loading, error } = useQuery(FETCH_POST_QUERY, {
variables: { postId: postId },
})
useMutation에서 했듯이 해당 Query에 대하여 variables를 변수로 넣어주면 된다.
const { data, loading, error, refetch } = useQuery(FETCH_POST_QUERY)
이런 방식으로 선언한 뒤, update를 시킬 addPostHandler와 같은 함수 마지막에 넣어주면 데이터가 추가됨과 동시에 refetching이 일어나 (따끈따끈하게) 추가된 데이터와 함께 전체 데이터가 랜더링 될 것이다.
그런데 여기서 한가지 의문점이 들 수 있다.
만약 랜더링 해야 할 데이터가 1억개라면?
1개의 데이터를 추가하기 위해 1억개의 데이터를 다시 랜더링해야 하는 비효율의 끝을 경험할 수도 있다. 이제 드디어 graphql의 캐시가 필요한 상황이 왔다.
const client = new ApolloClient({
...
cache: new InMemoryCache(),
})
먼저, 기존 client에서 설정한 코드를 통해 cache를 사용할 수 있다. 이를 토대로 createPost하는 곳의 mutation으로 넘어가보자.
export const CREATE_POST_MUTATION = gql`
mutation createPost($body: String!) {
createPost(body: $body) {
id
body
createdAt
username
...
}
}
`
...
const [createPost] = useMutation(CREATE_POST_MUTATION)
mutation을 export 후 해당 컴포넌트에서 useMutation hook을 사용하는 것까지는 기존 로그인과 동일하다. 하지만 지금 필요한 것은 cache를 사용해야 한다는 점이다.
왜? 불필요한 refetch를 방지하며 바로바로 사용자에게 데이터가 변경되었다는 것을 보여주기 위해서!
이 때 useMutation의 update를 사용할 수 있다.
const [createPost] = useMutation(CREATE_POST_MUTATION, {
update(cache, { data: { createPost } }) {
const { getPosts } = cache.readQuery<any>({
query: FETCH_POSTS_QUERY,
})
cache.writeQuery({
query: FETCH_POSTS_QUERY,
data: { getPosts: [body, ...getPosts] },
})
},
variables: values,
})
(일단 믿고 코드먼저 한번 보시라니깐요)
variables부분은 기존에서 변수를 넣어주는 것과 동일하다. 새로 추가된 것은 update의 cache, readQuery, writeQuery인데, 이것들에 대해 알아보도록 하자.
cache는 캐시 메모리의 그 캐시개념과 같다. 그래서
cache.readQuery란, 캐시에 있는 query를 그대로 가져온다는 의미이고,
cache.writeQuery란, 캐시에 있는 query에 새로운 데이터를 작성한다는 의미로 볼 수 있다.
따라서 이 코드가 어떤 의미인가 하니,
const { getPosts } = cache.readQuery<any>({
query: FETCH_POSTS_QUERY,
})
// FETCH_POSTS_QUERY를 통한 데이터를 불러온다, 이 때 데이터는 getPosts
cache.writeQuery({
query: FETCH_POSTS_QUERY,
data: { getPosts: [body, ...getPosts] },
})
// FETCH_POSTS_QUERY를 통한 데이터를 수정 혹은 덮어씌운다.
// 이 때 데이터는 새로운 body와 기존 getPosts데이터의 spread 연산자의 합
라고 아주 간결하게나마 설명할 수 있다.
따라서 새로 작성한 포스트는, 기존 포스트의 가장 맨 위 부분에 삽입될 것이다.
이렇게 cache update는 refetch를 사용하기 어려운 경우에 캐시를 이용하여 구현 가능하다.
이렇게
- GraphQL에 대한 쿼리문 작성,
- client에서 적용하는 방법,
- read, wirte등의 불러오기와 수정하기 기능
에 대하여 아주 간단하게나마 구현해 보았다. 아직 익숙하지도 않고 리팩토링 할 부분이 굉장히 많아 보이지만 쓰임새에 대하여 알아 보았을 때 좀 더 구체적인 명시가 가능하다는 부분은 확실히 장점으로 다가왔다. 추후에도 좀 더 공부할 기회가 있다면 자유자재로 사용할 수 있는 기술로 만들어보고 싶다!