optimistic update

비얌·2023년 8월 28일
0
post-thumbnail

🧹 개요

투두리스트의 CRUD 기능을 Firebase와 연동하다가, 투두 수정 부분에서 문제가 있었고 이를 해결하기 위해 optimistic update를 적용한 내용을 포스팅해보려고 한다.


💥 문제 상황

원래는 투두를 수정하고 완료 버튼을 누르면 바로 수정 후의 내용이 반영되어야 한다. 하지만 아래처럼 수정 전의 내용이 잠깐 나타난 후에야 수정 후의 내용이 반영이 되는 문제가 생겼다.


🔎 가정

새로운 데이터가 도착하기 전에 화면을 그려줘야 해서 이전 데이터를 보여줬을 것이다

코드의 어느 부분이 저렇게 수정 전의 내용을 잠깐 보여주는 건지 궁금했다.

그래서 일단 코드를 분석해보기로 했다. 먼저, handleTodoEdit 코드를 두 부분으로 나눠보았다.

const handleTodoEdit = async (updatedText, id) => {
  // 1 - 수정된 내용을 Firebase에 반영한다
  const todoItemRef = doc(db, "todoItem", id);
  await updateDoc(todoItemRef, {
    text: updatedText,
  });
  
  // 2 - Firebase에서 반영된 데이터를 가져와서
  //     리액트 내부의 상태를 업데이트한다
  syncTodoItemWithFirestore();
};
  • 1) 수정된 내용을 Firebase에 반영한다
  • 2) Firebase에서 반영된 데이터를 가져와서 리액트 내부의 상태를 업데이트한다

그다음 중간중간 콘솔을 찍어봤는데, Firebase에 있는 내용과 투두리스트의 상태가 다른 것을 확인할 수 있었다.

이때 새로운 데이터가 도착하기 전에 화면을 그려줘야 해서 이전 데이터를 보여줬던 게 아닐까? 하는 가정을 하게 되었다.

syncTodoItemWithFirestore 함수 - Firebase에 있는 데이터와 화면에 보여지는 투두리스트 상태를 동기화하는 함수이다

const syncTodoItemWithFirestore = () => {
  getDocs(collection(db, "todoItem")).then((querySnapshot) => {
    const firestoreTodoItemList = [];
    querySnapshot.forEach((doc) => {
      firestoreTodoItemList.push({
        id: doc.id,
        text: doc.data().text,
        isFinished: doc.data().isFinished,
      });
    });
    setDisplayInputs(firestoreTodoItemList);
  });
};

💻 검증

새로운 데이터가 도착하기 전에 화면을 그려줘야 해서 이전 데이터를 보여줬을 거라는 가정을 검증하기 위해 syncTodoItemWithFirestore 함수에 setTimeout을 걸어보기로 했다.

그 결과, 정말 syncTodoItemWithFirestore 함수가 실행되기 전에 화면에 보여지는 투두리스트 상태가 업데이트되지 않았음을 알 수 있었다. 그래서 수정 직후에는 Firebase의 데이터를 미처 받아오지 못했기 때문에 잠깐 이전 데이터가 보였던 것이다.

이런 상황은 아래와 같이 정리할 수 있는데, 4번에 와서야 화면에 수정된 내용이 보이는 것인데 나는 2번부터 보여주고 싶었던 것이다.

  1. 화면에는 원래 수정 전 투두 a가 있다.(이때 화면에는 a가 보임)
  2. 수정된 데이터 b를 서버로 보냄(이때 화면에는 a가 보임)
  3. 서버에서 데이터를 가져옴(이때 화면에는 a가 보임)
  4. 화면에 보여줄 상태를 서버에서 가져온 데이터 b로 업데이트함(이때 화면에는 b가 보임)

✨ 해결

그래서 먼저 화면에 나타나는 투두리스트의 상태를 업데이트하고 Firebase에서 데이터를 받아오도록 코드를 수정했다.

기존 코드

const handleTodoEdit = async (updatedText, id) => {
  const todoItemRef = doc(db, "todoItem", id);
  await updateDoc(todoItemRef, {
    text: updatedText,
  });
  syncTodoItemWithFirestore();
};

해결한 코드

const handleTodoEdit = async (updatedText, id) => {
  setDisplayInputs(
    displayInputs.map((todo) => {
      if (todo.id === id) {
        return {
          ...todo,
          text: updatedText,
        };
      }
      return todo;
    })
  );
  handleEditSync(updatedText, id);
};

const handleEditSync = async (updatedText, id) => {
  const todoItemRef = doc(db, "todoItem", id);
  await updateDoc(todoItemRef, {
    text: updatedText,
  });
  syncTodoItemWithFirestore();
};

🎈 결과

위의 코드로 해결했다.


🌳 회고

처음에는 어떻게 해결해야할지 막막했는데 가정을 세우고 차근차근 검증해나가다보니 해결할 수 있어서 뿌듯했다.

그리고 이렇게 해결한 방법이 optimistic update라고 불린다는 걸 나중에 알게 되었는데, 이 상황에 딱 맞는 용어를 알게되어서 기뻤다!

다만 여기서 이 방법이 맞는 것인지는 잘 모르겠어서 더 알아봐야겠다.

profile
🐹강화하고 싶은 기억을 기록하고 공유하자🐹

0개의 댓글