[React + GraphQL] 미니 프로젝트 일지(3)

GONI·2021년 12월 14일
0

GraphQL

목록 보기
4/4
post-thumbnail

이번에는 useQuery와 useMutation의 캐시에 대해 다뤄보려고 한다.
먼저 세상 너무나도 간단한 useQuery에 대해 알아보자


게시물 불러오기

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를 변수로 넣어주면 된다.


그렇다면 데이터를 추가하고 해당 데이터를 바로 보여줘야 하는 경우 이전 게시물에서 useQuery의 refetch를 사용하면 된다고 했다. refetch 역시 간단하게 사용할 수 있는데,
const { data, loading, error, refetch } = useQuery(FETCH_POST_QUERY)

이런 방식으로 선언한 뒤, update를 시킬 addPostHandler와 같은 함수 마지막에 넣어주면 데이터가 추가됨과 동시에 refetching이 일어나 (따끈따끈하게) 추가된 데이터와 함께 전체 데이터가 랜더링 될 것이다.


그런데 여기서 한가지 의문점이 들 수 있다.

만약 랜더링 해야 할 데이터가 1억개라면?

1개의 데이터를 추가하기 위해 1억개의 데이터를 다시 랜더링해야 하는 비효율의 끝을 경험할 수도 있다. 이제 드디어 graphql의 캐시가 필요한 상황이 왔다.


Cache

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를 사용하기 어려운 경우에 캐시를 이용하여 구현 가능하다.



이렇게

  1. GraphQL에 대한 쿼리문 작성,
  2. client에서 적용하는 방법,
  3. read, wirte등의 불러오기와 수정하기 기능

에 대하여 아주 간단하게나마 구현해 보았다. 아직 익숙하지도 않고 리팩토링 할 부분이 굉장히 많아 보이지만 쓰임새에 대하여 알아 보았을 때 좀 더 구체적인 명시가 가능하다는 부분은 확실히 장점으로 다가왔다. 추후에도 좀 더 공부할 기회가 있다면 자유자재로 사용할 수 있는 기술로 만들어보고 싶다!

profile
오로지 나의 기억력을 위한 일지

0개의 댓글