inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

한 입 크기로 잘라먹는 React.js 실전 프로젝트 - SNS 편

(4.10) 캐시 데이터 다루기 4 - 낙관적 업데이트 (2)

onSettled와 setQueryData

155

박선우

작성한 질문수 3

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,
      });
    },
  });
}

react typescript react-query supabase zustand

답변 1

1

이정환 Winterlood

안녕하세요 박선우님 이정환입니다.

결론부터 말씀드리면, 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는 단순히 중복이 아니라 “낙관적 업데이트 + 실패 시 롤백”을 위한 필수 구성 요소라고 이해하는 편이 더 정확합니다.

1

박선우

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

0

유혁스쿨

이정환 선생님 저도 해당 부분에 대해 추가 질문이 있습니다.


만약 화면에는 이미 백엔드 싱크가 꼬인 데이터로 출력된 상태에서 체크박스를 눌렀는데

onSettled에서 무결한 상태의 데이터싱크를 맞추기 위해 다시 리패칭해서 사용자가 적용한게 해제된다면,

이건 오류로 인한 이전값으로 되돌리는것도 아니라서, 사용자입장에서 더 햇갈릴수도 있는 상황이 발생될수도 있지 않을까요?

그래서 제 생각에는 onSettled가 호출되었을때 onMutate가 반환하는 낙관적 업데이트 완료 이전의 캐시 데이터와 서버상의 데이터의 싱크가 맞지 않을 경우 사용자가 해당 내용을 알 수 있도록 알려주고 난 후에 리패칭이 되도록 하는게 맞지않을까 생각이 드는데 onSettled의 매개변수등으로 처리가 가능할까요?

Zustand의 Store 타입은 어떤 기준으로 작성하는 건가요?

0

40

2

포스트 작성 중 이탈 방지 기능 (취소 버튼 클릭 시 오류)

0

73

2

"포스트 생성에 실패했습니다" 실패 문구와 함께 에러가 발생합니다ㅠㅠ

0

70

2

강의자료는 어떻게 제작하시나요?

0

86

2

비밀번호 재설정 1회용 이메일 링크

0

84

1

강의 수강 후 포트폴리오 준비 방향에 대해 조언 부탁드립니다.

2

94

1

회원가입 구현 (구현 후 최종 화면 출력 X)

0

112

2

(6.11) 회원가입시 프로필 정보 자동 생성하기 Q. 호출 순서 문의

0

82

1

명시적 타입 선언(콜론 타입 선언)과 as 타입 단언 차이

0

88

2

useMutation 적용 후 새로운 글 등록시 content가 안보여요

0

96

2

6.8 zustand 세션 질문입니다.

0

151

2

next.js 강의에서도 리액트 라이브러리들을 다뤄주시나요?

0

124

2

shadcn에서 radix ui와 base ui 차이는 뭔가요?

1

724

2

updateTodo 함수 생성시 화살표 함수 사용 안하는 이유

0

114

2

4.11 바로 투두 삭제가 안됨 질문

0

141

3

매개변수 updatedTodo 관련 질문

0

106

3

인증 정보가 만료되었을 때 라우트 가드 처리가 궁금합니다!

0

125

2

supabase를 사용하지 않을 경우 세션 데이터의 변경을 감지하고 스토어에 보관하는 방법이 궁금합니다!

0

118

2

4.6 id를 string으로 변경시 오류

0

112

2

리액트 타입스크립트 관련 질문있습니다.

0

98

1

소셜 로그인 구현하기 관련하여 질문이 있습니다!

0

151

2

ui 파일 질문드립니다.

0

117

1

tanstack query devtools에서 질문있습니다!

0

103

2

댓글 삭제 시 isPending 질문

0

89

2