인프런 커뮤니티 질문&답변
onSettled와 setQueryData
해결된 질문
작성
·
72
0
안녕하세요 선생님!
낙관적 업데이트(2) 강의 마지막에 onSettled를 통해 update가 종료된 후 기존 캐시 무효화 후 db에서 다시 받아와서 갱신시켜주는 로직을 작성한걸로 알고있는데
이때 onMutate 내부에서 setQueryData를 통해 별도의 업데이트된 정보를 캐시에 세팅시켜주는 로직은 필요가 없어지게 되는건가용?
export function useUpdateTodoMutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateTodo,
onMutate: async (updatedTodo) => {
await queryClient.cancelQueries({
queryKey: QUERY_KEY.todo.list,
});
const prevTodos = queryClient.getQueryData<Todo[]>(QUERY_KEY.todo.list);
/* 이부분 */
queryClient.setQueryData<Todo[]>(QUERY_KEY.todo.list, (prevTodo) => {
if (!prevTodo) return [];
return prevTodo.map((item) =>
item.id === updatedTodo.id ? { ...item, ...updatedTodo } : item,
);
});
/* //이부분 */
return {
prevTodos,
};
},
onError: (error, variable, context) => {
if (context && context.prevTodos) {
queryClient.setQueryData<Todo[]>(
QUERY_KEY.todo.list,
context.prevTodos,
);
}
},
onSettled: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEY.todo.list,
});
},
});
}답변 1
0
안녕하세요 박선우님 이정환입니다.
결론부터 말씀드리면, onSettled에서 invalidateQueries를 호출하기 때문에 최종적으로는 항상 백엔드 서버에서 최신 데이터를 다시 불러와 캐시를 갱신합니다. 따라서 “최종 데이터 동기화” 관점에서만 보면 onMutate 내부의 setQueryData가 없어도 결과 자체는 맞게 들어갑니다.
하지만 그렇다고 해서 onMutate에서의 setQueryData 로직이 의미가 없어지는 것은 아닙니다. 역할이 완전히 다릅니다.
1) onMutate의 setQueryData
onMutate에서 setQueryData를 호출하는 이유는 낙관적 업데이트를 통해 서버 응답을 기다리지 않고 화면에 먼저 반영하기 위한 것입니다.
사용자는 버튼을 클릭하는 즉시 목록에서 todo가 수정된 것처럼 보게 되고, 이후 네트워크 응답을 기다리거나 스피너를 보지 않아도 됩니다. 즉, “사용자 경험 향상”을 위한 단계입니다.
2) onError에서의 롤백
onMutate에서 prevTodos를 반환하는 이유는, mutation이 실패했을 때 onError에서 이전 상태로 되돌리기 위해서입니다. 만약 onMutate에서 setQueryData로 낙관적 업데이트를 하지 않는다면 사실상 롤백할 필요도 없어지고, prevTodos를 들고 있을 이유도 줄어듭니다. 그만큼 “실패 시 복구 UX”도 함께 사라지는 셈입니다.
3) onSettled + invalidateQueries
onSettled에서 invalidateQueries를 호출하는 부분은 낙관적 업데이트로 잠시 앞서 나간 캐시 상태를 최종적으로 서버의 실제 데이터와 다시 맞추기 위한 단계입니다.
즉,
onMutate → 낙관적 업데이트(임시 반영)
onError → 실패 시 롤백
onSettled → 서버 데이터로 최종 동기화
이렇게 세 단계가 서로 보완 관계에 있습니다.
정리하면 onSettled에서 invalidateQueries를 사용하기 때문에 “결과만 맞게 들어가는지”라는 관점에서는 onMutate의 setQueryData가 필수는 아닙니다.
다만 그 경우에는 낙관적 업데이트 자체가 사라지므로, 사용자는 서버 응답이 올 때까지 화면 변화가 늦게 반영되는 경험을 하게 됩니다. 따라서 질문 주신 코드에서 onMutate 내부의 setQueryData는 단순히 중복이 아니라 “낙관적 업데이트 + 실패 시 롤백”을 위한 필수 구성 요소라고 이해하는 편이 더 정확합니다.





자세하게 설명해주셔서 감사합니다😁