Optimistic Update (2) — 서버 요청 실패 시 롤백 전략

INNOPER·2025년 3월 17일

6️⃣ 문제: 서버 요청 실패 시 ⚠️

Optimistic Update를 적용하면
서버 응답 전에 UI가 먼저 업데이트되어 반응이 즉각적입니다.

하지만 현실적으로는,

  • 네트워크 오류
  • 서버 응답 지연
  • 권한 문제

등으로 인해 서버 요청이 실패할 수도 있습니다.

이 경우,
이미 UI는 바뀐 상태이므로 원래 상태로 롤백(rollback) 해야 합니다.


7️⃣ onError를 통한 롤백 구현 🔄

React Query의 useMutation은
onError 콜백을 통해 서버 요청 실패 시점을 잡을 수 있습니다.

이를 활용하면 Optimistic Update 후 캐시를 다시 원래 값으로 복구할 수 있습니다.


🧩 코드 예시

const { mutate } = useMutation({
  mutationFn: toggleLinkPin,

  // ✅ 성공 시: 서버 응답으로 최종 확정
  onSuccess: (response, { linkId, boxId }) => {
    queryClient.setQueryData(["linkDetail", linkId, boxId], (old) =>
      old
        ? { ...old, data: { ...old.data, isPin: response.data.isPin } }
        : old
    );
  },

  // ⚠️ 실패 시: 원래 상태로 롤백
  onError: (_err, { linkId, boxId, currentPinState }) => {
    // 1️⃣ LinkDetail 캐시 롤백
    queryClient.setQueryData(["linkDetail", linkId, boxId], (old) =>
      old
        ? { ...old, data: { ...old.data, isPin: currentPinState } }
        : old
    );

    // 2️⃣ List 캐시도 롤백
    queryClient.setQueriesData<InfiniteData<ListResponse>>(
      { queryKey: ["list", boxId], exact: false },
      (old) =>
        old
          ? {
              ...old,
              pages: old.pages.map((page) => ({
                ...page,
                items: page.items.map((item) =>
                  item.type === "link" && item.id === linkId
                    ? { ...item, isPin: currentPinState } // 🔙 원상 복구
                    : item
                ),
              })),
            }
          : old
    );

    // 3️⃣ 사용자에게 알림
    showError("핀 변경에 실패했습니다");
  },
});

📦 currentPinState 전달하기

Optimistic Update 시점에는 이미 isPin 상태가 토글된 상태이기 때문에,
실패 시 복구할 원래 값(currentPinState)을 mutate()에 함께 넘겨야 합니다.

전달

mutate({ linkId, boxId, currentPinState });

적용

onError: (_err, variables) => {
  const { currentPinState } = variables; // ✅ 최신 값 보장
  queryClient.setQueryData(...);          // 롤백 처리
}

이렇게 하면,
서버 요청이 실패하더라도 변경 전 상태로 즉시 복구할 수 있습니다.


✅ 정리

항목설명
Optimistic Update서버 응답 전에 UI를 먼저 변경
문제점요청 실패 시, UI는 이미 변경된 상태
해결법onError에서 currentPinState로 원래 상태 복원
롤백 대상LinkDetail, List(무한스크롤 캐시 모두)
UX 보완showError() 등으로 사용자에게 알림

🎯 결론

  • Optimistic Update는 UX를 빠르게 만들어주지만,
    실패 대비 롤백 전략이 없으면 데이터 불일치가 발생할 수 있습니다.

  • 따라서 항상 다음 구조를 유지하세요:

mutate(variables, {
  onMutate: optimisticUpdate,
  onError: rollbackUpdate,
  onSuccess: confirmServerState,
});

낙관적 업데이트는 "즉시 반응하는 UX"와 "데이터 일관성"의 균형이 핵심입니다.

profile
FE 개발자 INNOPER 입니다

0개의 댓글