낙관적 업데이트
낙관적 업데이트란 UX를 위해 빠르게 UI를 갱신하거나 업데이트하는 방법 중 하나로, 어떤 작업에 대한 응답을 서버로부터 받기 전에 해당 응답이 성공적으로 돌아올 것이라고 가정(낙관적)해서 먼저 업데이트를 진행한다. 만약 실패한다면 실행했던 낙관적 업데이트를 롤백하면 된다.
여태까지 작업했던 웹 애플리케이션은 이벤트를 CRUD하는 구조였는데, 이벤트의 제목을 수정하면 새로고침을 해야만 수정된 내용이 반영되는 불편함이 있다. 우리는 수정하고 [확인]버튼을 누르면 바로 수정된 내용이 반영되어 보여지길 원한다. 이 때 onSuccess로 쿼리를 무효화 시켜서 구현하려고 하면 변형이 완료될 때까지 기다려야 하는 불편함이 있다. 또 이벤트의 제목이 빈 문자열이면 에러가 뜨기 때문에 이 부분을 고려해서 제목을 수정하면 바로 낙관적 업데이트를 진행하고, 빈 문자열이라면 롤백하는 기능을 구현하고자 한다.
export default function EditEvent() {
const navigate = useNavigate();
const params = useParams();
const { data, isPending, isError, error } = useQuery({
queryKey: ['events', params.id],
queryFn: ({ signal }) => fetchEvent({ signal, id: params.id }),
});
const { mutate } = useMutation({
mutationFn: updateEvent,
onMutate: async data => {
const newEvent = data.event;
await queryClient.cancelQueries({ queryKey: ['events', params.id] });
const previousEvent = queryClient.getQueryData(['events', params.id]);
queryClient.setQueryData(['events', params.id], newEvent);
return { previousEvent };
},
onError: (error, data, context) => {
queryClient.setQueryData(['events', params.id], context.previousEvent);
},
onSettled: () => {
queryClient.invalidateQueries(['events', params.id]);
},
});
function handleSubmit(formData) {
mutate({ id: params.id, event: formData });
navigate('../');
}
function handleClose() {
navigate('../');
}
먼저 낙관적 업데이트는 변형을 하기 때문에 useMutation에서 진행된다.
mutationFn는 이벤트를 업데이트 시키는 updateEvent가 될 것이고, onMutate에서 변형이 발생한다.
data를 받고 handleSubmit으로 제출된 data를 newEvent로 명명했다.
newEvent를 받은 후 변형을 시켜야 하니 비동기로 작업을 하는데, 기존의 쿼리를 cancel 시켜야 한다.
에러가 발생했을 때 refetch가 일어나지 않게 해 롤백 상태를 유지해야 하기 때문에 이 부분에선 cancel을 시켜준다.
그래서 지금 반영되지 않은, 가장 최신 버전의 data를 getQueriesData로 받아와 previosEvent에 저장한다.
제대로 변형이 된다면 newEvent를 setQueryData를 사용해 새로 적용할 수 있고,
만약 실패한다면 아까 previousEvent로 저장한 값으로 롤백해야 한다.
그리고 완료된 후에는 이벤트 데이터에 대한 캐시를 무효화 해 트리거 하기 위해 onSettled로 쿼리를 무효화한다.
간단하게 비동기로 새로 들어온 데이터를 받고, 방금 막 들어온 데이터와 지금 가장 최신인 데이터를 가져온 뒤 변형이 되면 방금 막 들어온 데이터로 갈아주고 변형 시 에러가 나면 롤백을 하기 위해 가장 최신의 데이터를 넣어주고, 변형이 일어나든 에러가 나든 동작이 완료되면 새로 refetch를 해주면 되는 것이다.
뭔가 어려워 보였는데 경우에 따라 나누면 생각보다 간단하게 구현할 수 있는 것 같다.
'🌺 TanStack Query' 카테고리의 다른 글
[TanStack Query] Type has no properties in common with type invalidateQueries 에러 해결 (2) | 2024.06.20 |
---|---|
[TanStack Query] useMutation으로 데이터 변경/성공 시 동작 및 쿼리 무효화 (0) | 2024.05.03 |
[TanStack Query] 동적 쿼리와 enabled를 사용한 쿼리 활성/비활성화 (0) | 2024.04.30 |
[TanStack Query] useQuery 사용하기 (0) | 2024.04.30 |
[TanStack Query] TanStack Query(React Query) 이해하기 (1) | 2024.04.28 |