
1. Optimistic Update의 개념
낙관적 업데이트가 무슨 뜻일까요? 낙관적?긍정적?인 업데이트? 비슷합니다.
서버와의 REST API 통신이 성공한다는 가정 하에 클라이언트 캐싱 데이터를 조작하고 UI를 업데이트 하는 것이 Optimistic Update 개념의 전부입니다. 쉽게 말하자면 내가 보낸 요청이 무조건 '성공'했을 거라는 가정 하에 UI를 업데이트 해주는 것입니다. react-query는 Optimistic Update를 지원해줍니다!
1-1. 왜 굳이 낙관적 업데이트를 할까요?
간단합니다. 사용자의 UI 를 개선시키기 위함입니다. 예를 들어서 사용자가 게시물 좋아요 버튼을 눌렀을 때 화면에서 즉시 좋아요 ❤️가 활성화되게 할 수 있습니다. 서버의 응답결과는 시간이 조금 걸릴 지라도 요청이 성공한다는 가정 하에 유저에게는 즉각적으로 UI가 바뀌는 것처럼 보이게 함으로써 action에 대한 빠른 response를 보여줘서 결국 UI를 개선시킬 수 있는 셈이죠. 그런데 만약 미리 업데이트를 했는데 요청이 실패했다면 어떻게 해야 할까요? 밑의 코드 예시에서 살펴보도록 하겠습니다.
2. Optimistic Update 코드 예시
먼저, query의 결과에 따라 인자로 들어오는 data, error, variables, context 에 대해서 알아야 할 필요가 있습니다.
해당 인자들은 쿼리의 각 메소드마다 들어오는 종류가 다릅니다. ex) onSuccess, onError 등
onSettled를 기준으로 설명하고자 합니다.

1. data: data는 query의 성공 결과를 담고 있습니다.
2. error: 에러는 에러 발생 시 에러 결과를 담게 됩니다.
3. variables: variables는 금방 mutate 요청으로 보낸 payload를 담고 있습니다.
4. context는 onMutate에서 return 한 값을 담고 있습니다.(이게 핵심입니다.)
[예시 코드]
import React from 'react'
import { useMutation, useQueryClient } from 'react-query'
const OptimisticUpdate = () => {
const queryClient = useQueryClient()
const todos = 'todos 추가하는 api 함수라고 가정합시다 ㅎㅎ;'
const mutation = useMutation(todos, {
onMutate: async(newTodo) => {
await queryClient.cancelQueries('todos');
const previousTodos = queryClient.getQueryData('todos');
queryClient.setQueryData('todos', (oldData)=>{
if(!oldData){
return [];
}
return [
...oldData,
{id: oldData.at(-1).id + 1, todo: newTodo, done: false},
]
})
return { previousTodos };
},
onError: (error, newTodo, context) => {
queryClient.setQueryData('todos', context?.previousTodos)
},
// 오류 또는 성공 후에는 항상 리프레쉬
onSettled: () => {
queryClient.invalidateQueries('todos');
}
})
const onSubmit = (e) => {
e.preventDefault()
mutation.mutate('newTodos 내용')
}
return (
<div>
<form onSubmit={onSubmit}>
...
<button>제출</button>
</form>
</div>
)
}
export default OptimisticUpdate
1. onMutate는 mutate query function이 실행되기 전에 호출됩니다.
2. 이 때 cancelQueries('todos')를 하는 이유는 Optimistic Update로 이미 변경된 데이터가 fetch로 또 변경되는 것을 방지하기 위함입니다.
3. getQueryData로 이전 todos 데이터를 저장해두어 이후 api 요청이 실패할 경우에 사용할 수 있도록 저장을 해둡니다.
4. (성공과 실패에 따라 onSuccess 혹은 onError를 먼저 실행) 에러가 발생할 경우 onError의 context 객체에서 previousTodos를 뽑아 다시 이전 데이터로 되돌려줍니다.
5. onSettled가 성공여부에 상관없이 가장 마지막에 실행됩니다. 결과적으로 onMutate -> onSettled 혹은 onMutate -> onError -> onSettled의 순서로 실행됩니다.
onSettled 에 있는 invalidateQueries('todos') 가 최종적으로 'todos' 라는 쿼리키를 갖는 쿼리를 무효화시켜서 refetch 하도록 하여 데이터를 서버와 일치시켜줍니다. (쉽게 말해서 성공, 실패 여부에 상관없이 실제 todo 리스트에 대한 데이터를 가져오는 것으로 마무리가 됩니다.)
참고한 블로그
[React Query] Query / Mutation 이해하기
1. Query 상태 흐름 이해하기 https://codesandbox.io/s/react-query-sangtae-heureum-oyig4o react-query(상태 흐름) - CodeSandbox react-query(상태 흐름) by oyg0420 using axios, react, react-dom, react-query, react-scripts, styled-components, typ
oyg0420.tistory.com
https://devkkiri.com/post/7fafd5b1-f034-47a6-8f4b-201701f8f991
React Query(리액트 쿼리) 개념 및 예제(6) | Kkiri Blog
지난 포스팅에서 useMutation의 세 가지 ...
devkkiri.com