React

[React] Tanstack Query useMutation 사용해 보기

woo.oing 2025. 10. 30. 16:41

아래 강의를 수강하며 배운 내용을 정리한 글입니다

[한 입 크기로 잘라먹는 실전 프로젝트 - SNS 편 - 이정환] https://inf.run/SbJ8w

 

 

← 이전 글 보기

 

[React] Tanstack Query 사용해 보기

아래 강의를 수강하며 배운 내용을 정리한 글입니다[한 입 크기로 잘라먹는 실전 프로젝트 - SNS 편 - 이정환] https://inf.run/SbJ8w 가장 대표적인 서버 상태 관리 라이브러리인 tanstack query를 사용해보

woooing.tistory.com

 

 

데이터 조회(get)를 관리하는 useQuery 외에도 데이터 수정(post..) 시에는 useMutation 을 사용합니다

  const { mutate, isPending } = useMutation({
    mutationFn: createTodo,
    onMutate: () => {},
    onSettled: () => {},
    onSuccess: () => {
      location.reload();
    },
    onError: (error) => {
      alert(error.message);
    },
  });

 

 

useMutation에서 대표적으로 사용할 수 있는 함수들은 아래와 같습니다

mutationFn mutate 호출 시 실행할 함수
onMutate 요청 발송 시
onSettled 요청 종료 시 (성공, 실패 모두)
onSuccess 요청 성공 시
onError 요청 실패 시

 

 

새로운 todo를 생성했을 때 todolist 데이터도 업데이트를 해야 목록에서 새로 추가한 데이터가 보여지기 때문에

이경우엔 아래와 같은 방법을 사용하여 특정 queryKey의 데이터를 업데이트 할 수 있습니다

 

const queryClient = useQueryClient();

 

 

1. invalidateQueries

  queryClient.invalidateQueries({
    queryKey: QUERY_KEYS.todo.list,
  });

 

 

2. setQueryData

  queryClient.setQueryData<Todo[]>(QUERY_KEYS.todo.list, (prevData) => {
    if (!prevData) return [newData];
    return [...prevData, newData];
  });

 

 

3. 낙관적 업데이트 (Optimistic Update)

인스타그램의 '좋아요' 기능처럼 실제 서버에 요청 보내기 전에 성공할 거라는 가정으로 화면의 상태를 먼저 수정하여 보여주는 경우 사용할 수 있습니다

 

'좋아요'가 눌리는 순간 onMutate에서 화면 상태를 먼저 업데이트 해준 후 이전 값을 리턴

onError에서는 onMutate에서 넘겨준 값을 context 값으로 받아 요청이 실패할 경우 이전 상태로 복구

onSettled에서는 요청이 종료되면 서버에서 최신 데이터 조회

export function useUpdateTodoMutation() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: updateTodo,
    onMutate: async (updatedTodo) => {
      // 1. 진행 중인 쿼리 취소
      await queryClient.cancelQueries({
        queryKey: QUERY_KEYS.todo.list,
      });

      // 2. 낙관적 업데이트
      const prevTodos = queryClient.getQueryData<Todo[]>(QUERY_KEYS.todo.list);
      queryClient.setQueryData<Todo[]>(QUERY_KEYS.todo.list, (prevTodos) => {
        if (!prevTodos) return [];
        return prevTodos.map((prevTodo) =>
          prevTodo.id === updatedTodo.id
            ? { ...prevTodo, ...updatedTodo }
            : prevTodo,
        );
      });

      return {
        prevTodos,
      };
    },
    onError: (error, variable, context) => {
      // 3. 실패시 데이터 원상복구
      if (context && context.prevTodos) {
        queryClient.setQueryData<Todo[]>(
          QUERY_KEYS.todo.list,
          context.prevTodos,
        );
      }
    },
    onSettled: () => {
      // 4. 서버와 클라이언트 데이터 동기화
      queryClient.invalidateQueries({
        queryKey: QUERY_KEYS.todo.list,
      });
    },
  });
}

 

 

일반 수정 vs 낙관적 업데이트

일반 수정: 사용자 수정 요청 → 서버 수정 요청   성공 목록 재조회 → 화면 반영

낙관적 업데이트: 사용자 수정 요청 → 화면 반영 → 서버 수정 요청 → [실패] 화면 이전 상태로 복구 → 목록 재조회 / [성공] 목록 재조회