좋아요 기능 구현 중에 서버 상태와 로컬 상태가 동기화 되는데에 항상 일정 시간이 필요함을 알게 되었습니다. 좋아요 버튼 클릭시 바로 반영되게 해보았지만, 이렇게 할 경우 중간에 문제가 생기거나 하여 서버에 반영되지 않았을 경우의 처리가 어려웠습니다. 이러한 문제를 해결하기 위해 기존에는 매번 상태를 수동으로 관리하려 했지만, 이는 코드의 복잡성을 증가시키고 유지보수를 어렵게 만드는 요인이 되었습니다.
이러한 상황에 대처하는 패턴이 존재했고, 이를 낙관적 업데이트(optimistic update)라고 한다는 것을 알게 되었습니다. tanstack-query는 이런 optimistic update를 비교적 쉽게 구현하게 해주고 있었습니다. 구현 방식은 유저가 좋아요 등을 클릭하면 서버에서 응답이 오기 전에 우선 낙관적으로 판단하여 UI 부터 업데이트 한 뒤, 만약 실패했다면 다시 롤백시키는 방법이었습니다.
mutationFn:
mutationFn
은 뮤테이션을 수행하는 비동기 함수입니다. 여기서는 addTodo
함수가 이 역할을 합니다.onMutate:
onMutate
는 뮤테이션이 시작될 때 호출됩니다.todos
상태를 가져와 이전 상태로 되돌릴 수 있도록 저장합니다.queryClient.setQueryData
를 사용하여 로컬 상태를 즉시 업데이트합니다.previousTodos
)는 onError
또는 onSettled
에서 사용할 수 있습니다.onError:
context
매개변수는 onMutate
에서 반환된 값을 포함합니다.onSettled:
invalidateQueries
를 사용하여 todos
쿼리를 무효화하여, 서버에서 최신 데이터를 다시 가져오도록 합니다.const addMutation = useMutation({
mutationFn: addTodo, // 실제 변이 함수
onMutate: async (newTodo) => {
console.log("onMutate 호출");
await queryClient.cancelQueries({ queryKey: ["todos"] }); // 쿼리 취소
const previousTodos = queryClient.getQueryData(["todos"]); // 현재 상태 저장
queryClient.setQueryData(["todos"], (old) => [...old, newTodo]); // 낙관적 업뎃
return { previousTodos }; // 이전 상태 반환
},
onError: (err, newTodo, context) => {
console.log("onError");
console.log("context:", context);
queryClient.setQueryData(["todos"], context.previousTodos); // 오류 시 이전 상태 복원
},
onSettled: () => {
console.log("onSettled");
queryClient.invalidateQueries({ queryKey: ["todos"] }); // 변이 후 쿼리 무효화
},
});
onError
핸들러를 통해 오류 발생 시 자동으로 상태를 복원함으로써, 에러 처리가 일관되고 효율적으로 이루어졌습니다.onSettled
에서 쿼리를 무효화하여 최신 데이터를 다시 패칭함으로써, 항상 최신 상태의 데이터를 유지할 수 있었습니다.낙관적 업데이트의 강점과 한계:
TanStack Query의 유연한 상태 관리:
useMutation
훅을 활용하여 복잡한 상태 관리 로직을 간단하게 처리할 수 있었습니다. 특히, onMutate
, onError
, onSettled
등의 생명주기 메서드를 통해 변이의 다양한 단계를 효과적으로 관리할 수 있었습니다.에러 핸들링의 중요성:
onError
메서드를 통해 이전 상태로 롤백함으로써 데이터 일관성을 유지할 수 있었습니다.데이터 일관성과 캐싱 관리: