👻 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 를 지정해 적절하게 원하는 방법으로 업데이트할 수 있다.
그러나 어떤 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
의 경우 그 과정이 필요하지 않다.
'개발 공부 > GraphQL & Apollo Client' 카테고리의 다른 글
Apollo Client - Cache 기본 개념 (0) | 2021.08.27 |
---|
댓글