[Final Project] 서버 사이드에서 업데이트 한 데이터와 클라이언트 사이드 데이터 동기화 오류

liinyeye·2024년 8월 8일
0

Project

목록 보기
39/44

🔎 문제 상황

채팅에서 투두리스트 저장 후 채팅 화면으로 이동했을 때 화면이 리렌더링 되지 않으면 ui가 업데이트 되지 않음

🤔 문제 분석

  • 서버 사이드와 클라이언트 사이드의 데이터 상태가 별도로 관리되어 동기화되지 않고 있음
  • 서버에서 데이터 업데이트 시 DB에는 바로 반영되지만, 클라이언트에서 관리하는 query 데이터는 리렌더링 시 업데이트되어 화면에 바로 반영되지 않음

클라이언트 사이드 비동기 데이터 상태 관리

const fetchTodos = async (user_id: string): Promise<Todo[]> => {
  const { data, error } = await supabase.from("todos").select("*").eq("user_id", user_id);
  if (error) throw new Error(error.message);
  return data ?? [];
};

const addTodo = async (todo: Partial<Todo>): Promise<Todo> => {
  const { data, error } = await supabase.from("todos").insert(todo).select().single();
  if (error) throw new Error(error.message);
  return data as Todo;
};

  const todosQuery = useQuery<Todo[], Error>({
    queryKey: ["todos", user_id],
    queryFn: () => fetchTodos(user_id)
  });

  const addTodoMutation = useMutation({
    mutationFn: addTodo,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["todos"] });
    }
  });

서버 사이드 비동기 데이터 업데이트

const saveChatTodoItems = async (supabase: SupabaseClient, sessionId: string, items: string[]) => {
  const {
    data: { user },
    error: userError
  } = await supabase.auth.getUser();

  if (userError || !user) {
    console.error("Error getting User Data", userError);
    throw userError;
  }

  const todoToInsert = items.map((item) => ({
    todo_id: uuid4(),
    created_at: new Date().toISOString(),
    todo_title: item,
    todo_description: null,
    user_id: user.id,
    address: { lat: 0, lng: 0 },
    event_datetime: dayjs().set("hour", 0).set("minute", 0).toISOString(),
    is_done: false,
    is_chat: true,
    is_all_day_event: true
  }));

  const { data, error } = await supabase.from("todos").insert(todoToInsert).select();

  if (error) {
    console.error("Error saving chat todo items", error);
    throw error;
  }
  return data;
};

📝 해결 과정

시도 1

saveChatTodoItems함수(서버 사이드)에서 addTodoMutation을 사용
-> 동일한 쿼리를 업데이트 시켜서 상태를 일정하게 유지할 수 있도록 함.

문제점

React 훅인 커스텀 훅은 컴포넌트나 커스텀 훅 내부에서만 사용 가능하기 때문에 일반 함수 또는 서버 사이드에서는 해당 훅을 사용할 수 없음.

  • 해당 함수를 사용하기 위해서는 컴포넌트 내부로 이동 필요
  • 필요한 함수들을 인자로 전달받도록 수정이 필요
    -> 라우트핸들러(서버사이드)에서는 마찬가지로 addTodoMutation을 인자로 전달받을 수 없음

시도 2 (해결)

  • saveChatTodoItems함수에서는 원래대로 비동기로 supabase에 바로 데이터를 넣어줌.
  • 대신 클라이언트 사이드에서 채팅 데이터 상태를 업데이트 시켜주는 saveTodoMutation함수에서 onSuccess에 todos 쿼리를 무효화시키는 코드인 queryClient.invalidateQueries({ queryKey: ["todos", userId] }); 를 추가
  • 채팅 내용이 저장됨과 동시에 서버와 클라이언트 모두 todos 데이터가 바로 업데이트되어 동일한 상태를 유지할 수 있도록 해결.
  const saveTodoMutation = useMutation({
    mutationFn: async () => {
      const response = await fetch(`/api/chat/${aiType}/${sessionId}`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ saveTodo: true, currentTodoList, isResetButton: false })
      });
      if (!response.ok) {
        throw new Error("Failed to save todo list");
      }
      const data = await response.json();
      return data;
    },
    onSuccess: () => {
      const savedMessage = {
        role: "assistant" as const,
        content: "투두리스트 저장이 완료되었습니다. 저장된 내용을 투두리스트 페이지에서 확인해보세요!",
        created_at: new Date().toISOString(),
        showSaveButton: false
      };
      queryClient.setQueryData<MessageWithButton[] | undefined>(
        [queryKeys.chat, aiType, sessionId],
        (oldData): MessageWithButton[] | undefined => {
          if (!oldData) return [savedMessage];
          const updatedData = oldData.map((msg) => ({ ...msg, showSaveButton: false }));
          return [...updatedData, savedMessage];
        }
      );
      // 투두리스트 쿼리 무효화
      queryClient.invalidateQueries({ queryKey: ["todos", userId] });
      setCurrentTodoList([]);
      toast.success("투두리스트 페이지로 이동하기", {
        onClose: () => {
          router.push("/todo-list");
        }
      });
    },
    onError: (error) => {
      console.error("Error saving todo list :", error);
    }
  });
profile
웹 프론트엔드 UXUI

0개의 댓글