본문 바로가기
개발 공부/GraphQL & Apollo Client

Apollo Client - Cache 사용하기

by 호랭개발자 2022. 8. 14.
반응형


👻
Cache란?

HTTP의 캐싱

HTTP는 Cache 기능을 적용해 불필요한 리소스 재요청을 방지한다. 아래와 같은 여러 가지 Cache 방법을 가지고 있다.

HTTP는 이러한 정책들을 각각의 URL에 설정하는 형식으로 캐싱이 이루어진다. 즉, 각 엔드포인트마다 Cache 방법을 설정할 수 있다. Rest API는 HTTP의 기능을 모두 사용할 수 있기 때문에 캐싱 역시 가능하다.

그러나 GraphQL은 Endpoint가 하나(/graphql이라는 URL)이기 때문에 HTTP에서 제공하는 캐싱 기능을 사용할 수 없다. 그렇기 때문에 같은 리소스를 요청하더라도 실제로 네트워크를 거쳐야 한다. 이러한 점을 보완하기 위해 Apollo Client에서는 Caching 기능을 제공한다. 서버로부터 받아온 데이터뿐만 아니라 localStorage 나 apollo-client의 reactive Variables 에 저장되는 local-only field 역시 캐싱된다.

👻 Cache 로직 사용하기 

왜 Cache 로직이 필요한가?

Apollo Client의 cache는 사용자가 fetch-policy 를 지정해 적절하게 원하는 방법으로 업데이트할 수 있다.

Apollo Client - Cache 기본 개념

그러나 어떤 fetch-policy 규칙이든 mutate 가 일어나는 경우에는 새로운 데이터를 받아온 후 모든 cache를 업데이트하기 때문에 fetch-policy 만을 사용해서는 효율적인 방법으로 cache를 업데이트할 수 없다. 여기서 필요한 것이 cache를 직접 업데이트하는 방법이다. 

Cache 쓰기 / 읽기

Strategy API
GraphQL Queries readQuery/ writeQuery / updateQuery
GraphQL Fragment readFragment / writeFragment / updateFragment
Cache Directly cache.modify

Cache의 내용을 업데이트하기 위해서는 write 기능을 사용하여야 한다. 즉, read 기능으로 cache를 읽어오고 내용을 확인한 뒤 write 기능을 사용해 Cache를 업데이트하여 프로그램의 어디서든 같은 Cache의 내용을 읽을 수 있도록 할 수 있다. (read 기능으로 읽어온 데이터 값을 write 기능 없이 직접 변환하는 것은 Apollo Client의 Cache 내용에 영향을 주지 않는다.) 
write 메소드에서 실행되는 내용들은 client의 cache에만 적용될 뿐 실제 GraphQL 서버에 적용되는 내용은 아니다. 

✨ GraphQL Queries

Query를 이용해서 cache에 접근한다.

readQuery

variables에는 query를 읽는 데에 필요한 variables를 제공한다.

const READ_TODO = gql`
  query ReadTodo($id: ID!) {
    todo(id: $id) {
      id
      text
      completed
    }
  }
`;

const { todo } = client.readQuery({
  query: READ_TODO,
  variables: { // Provide any required variables here
    id: 5,
  },
});

READ_TODO 쿼리에 id를 넘겨주어야 하므로 위에서는 variables에 id를 제공했다. READ_TODO 쿼리는 todo의 id, text, completed 필드를 요청하는데 cache에 이 세 개의 값들 중 하나라도 존재하지 않으면 null을 반환한다. 

writeQuery

readQuery와 유사한 형태를 띄지만 data 영역이 추가된다.

client.writeQuery({
  query: gql`
    query WriteTodo($id: Int!) {
      todo(id: $id) {
        id
        text
        completed
      }
    }`,
  data: { // Contains the data to write
    todo: {
      __typename: 'Todo',
      id: 5,
      text: 'Buy grapes 🍇',
      completed: false
    },
  },
  variables: {
    id: 5
  }
});

writeToDo 쿼리가 id, text, completed 필드를 필요로 하므로 data에서 세 개의 필드 내용을 제공한다. 
writeToDo 쿼리가 새로운 todo를 생성하는 쿼리라고 하자. 위의 writeQuery 메소드는 id가 5인 todo를 생성한다. 
writeToDo 쿼리가 이미 존재하는 todo를 수정하는 쿼리라고 하자. writeQuery의 data에 제공된 필드의 값으로 기존의 todo가 덮어써진다. 

updateQuery
여기에서 설명

GraphQL Fragment

Fragment를 이용해서 cache에 접근한다. 완전히 모든 값이 cache에 있어야지 제대로 된 데이터를 반환하는 query를 사용한 cache 접근에 비해 좀 더 간편하다. 

readFragment

readQuery와는 다르게 readFragment는 cache를 식별하기 위한 id 값을 필수로 제공해야 한다. Apollo Client는 기본적으로 id 값을 <__typename>:<id>의 형태로 갖고 있다.

const todo = client.readFragment({
  id: 'Todo:5', // The value of the to-do item's cache ID
  fragment: gql`
    fragment MyTodo on Todo {
      id
      text
      completed
    }
  `,
});

 따라서 위에서는 Todo:5를 id 값으로 제공했다. 

writeFragment

readFragment와 비슷하지만 writeQuery와 같이 data 영역을 가진다.

client.writeFragment({
  id: 'Todo:5',
  fragment: gql`
    fragment MyTodo on Todo {
      completed
    }
  `,
  data: {
    completed: true,
  },
});

위의 writeFragment는 completed 속성을 true로 바꾸어 cache에 저장한다. 역시 cache를 식별하기 위한 id를 제공해야 한다.

updateFragment
여기에서 설명

Cache Update

cache를 읽고 쓰는 과정들을 한 번에 실행할 수 있는 `updateQuery`와 `updateFragment`가 존재한다. 이 두가지 메소드는 Apollo Clinet 3.5 이상의 버전에서 지원된다. 

updateQuery / updateFragment의 option들은 readQuery / readFragment와 동일하다. 이 option들 뒤에 update 함수를 적어주면 된다.

// Query to fetch all todo items
const query = gql`
  query MyTodoAppQuery {
    todos {
      id
      text
      completed
    }
  }
`;

// Set all todos in the cache as completed
cache.updateQuery({ query }, (data) => ({
  todos: data.todos.map((todo) => ({ ...todo, completed: true }))
}));

위에서 MyTodoAppQuery는 모든 todo를 가져오는 쿼리이다. option에 쿼리를 전달하고 받아오는 data를 update function으로 전달해 모든 todo object의 completed가 true로 설정한다. 이때 cache를 업데이트하고 싶지 않다면 update function에서 undefined를 반환하는 방식을 사용할 수 있다. 

👻 참고

useMutation의 optimisticResponse 옵션

cache를 사용하는 것은 서버와의 통신 과정을 생략해 좀 더 빠른 응답을 얻기 위해서이다. 응답이 오기 전에 임시적으로 보여주고 싶은 데이터가 있다면 optimisticResponse 옵션을 사용할 수 있다.

Apllo Client - Optimistic Response

cache의 writeData 함수

writeQuery 함수의 경우 쿼리 응답 스키마의 형식과 업데이트하려는 데이터의 형식이 같은지 확인하기 위해 query 파라미터를 넘겨주어야 하지만 writeData의 경우 그 과정이 필요하지 않다.

Apollo Client - writeData

 

반응형

'개발 공부 > GraphQL & Apollo Client' 카테고리의 다른 글

Apollo Client - Cache 기본 개념  (0) 2021.08.27

댓글