가현님 덕에 최고심에 빠져버렸다.. 채김져.. 미루고 미루던 삭제 구현하기. 그래도 수량 버튼을 useMutation으로 처리하면서 힌트를 얻어서 삭제도 useMutation을 통해 데이터를 업데이트하려고 한다. 

 

🧺 장바구니 1단계

 

[프로젝트] Next.js 장바구니 구현하기 - (1)

이번주 월요일부터 개발작업에 들어갔다. 와이어프레임이 거의 나와서(혜원님의 열일) 개발을 바로 시작할 수 있었다. 내가 맡은 기능은 아래와 같다. 우선 월요일에 특산물 전체보드와 상세페

ejunyang.tistory.com

🧺 장바구니 2단계

 

[프로젝트] Next.js 장바구니 구현하기 - (2)

장바구니에 담은 상품 수량 추가/감소 기능 구현하던 중 추가하면 아래 결제 금액도 변경이 되어야하는데 변경이 되지 않고 새로고침해야 변경이 되었다.. useState로 화면에 바로 렌더링되도록

ejunyang.tistory.com

🧺 장바구니 3단계

 

[프로젝트] Next.js 장바구니 구현하기 - (3)

수량을 누르면 총 결제금액이 오르는 것까지 구현했다. 근데 수량 버튼을 눌렀을 때 좀 느리게 브라우저에 반영되어서 optimsitic update로 구현해보려고한다.  [프로젝트] Next.js 장바구니 구현하기

ejunyang.tistory.com

 


 

Data-table-column-header.tsx

원래는 아래와 같은 로직으로 진행했다. 하지만 새로고침해야 삭제가 되었다.

Data-table을 임포트하고있는 CartList 컴포넌트에서 이미 카트 데이터를 useQuery로 관리하고 있기 때문에 업데이트는 모두 useMutation을 사용해주기로했다.

const deleteProduct = async (productId: string) => {
  const { data, error } = await supabase
    .from('cart')
    .delete()
    .eq('product_id', productId);
  if (error) {
    console.error('상품을 삭제하는데 실패했습니다.', error);
  } else {
    return data;
  }
};

 

 

 

이전에 카운트 버튼을 참고해서 구현했다. useDeleteProduct 훅을 생성하고 아래 쿼리문을 썼다. 원래는 함수로 만들었는데 빌드 테스트할때 에러가 났다. useQueryClient 훅이 deleteProduct 함수 안에서 호출되는 것은 React 훅 규칙에 위배된다는 에러였고, 훅은 반드시 React 함수 컴포넌트 또는 사용자 정의 훅 내에서만 호출해야한다는 오류였다.

Error: React Hook "useQueryClient" is called in function "deleteProduct" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use". react-hooks/rules-of-hooks

 

 

그래서 따로 useDeleteProduct 훅 파일을 만들어 빼놓고 임포트 시켰다.

const useDeleteProduct = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (productId: string) => {
      const { error } = await supabase
        .from('cart')
        .delete()
        .eq('product_id', productId);

      if (error) {
        throw new Error('상품을 삭제하지 못했습니다.' + error.message);
      }
    },
    onSuccess: () => {
       queryClient.invalidateQueries({
         queryKey: ['cart']
       });
     }
 })

 

 

데이터는 잘 삭제되지만 이것도 수량을 올릴때 결제 금액이 천천히 올랐던 것처럼 느리게 반영되는 이슈 발생.. 이것도 낙관적 업데이트를 해줘야하나보다.. useMutation으로 서버의 데이터를 수정할 땐 꼭 optimistic update를 해줘야할 것 같다.

 {
    id: 'delete',
    header: '',
    cell: ({ row }) => {
      const mutation = useDeleteProduct();

      return (
        <button onClick={() => mutation.mutate(row.getValue('product_id'))}>
          <CgClose className="text-[#959595]" />
        </button>
      );
    }
  }

 

여기서 또 나타난 빌드에러..☠️  columns가 배열로 되어있어서 const mutation = useDeleteProduct(); 을 컬럼 배열 안에 넣는 것은 훅 규칙에 위배된다는 말이었다. 훅은 반드시 리액트 컴포넌트 안에 호출해야한다고 한다.

Error: React Hook "useDeleteProduct" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function. react-hooks/rules-of-hooks

 

 

그래서 삭제버튼 컴포넌트를 만들어서 컬럼 배열에 추가해주었다.

const DeleteButton = ({ productId }: { productId: string }) => {
  const mutation = useDeleteProduct();

  const handleDelete = () => {
    if (confirm('해당 제품을 삭제하시겠습니까?')) {
      mutation.mutate(productId);
    }
  };

  return (
    <button onClick={handleDelete}>
      <CgClose className="text-[#959595]" />
    </button>
  );
};
{
    id: 'delete',
    header: '',
    cell: ({ row }) => {
      return <DeleteButton productId={row.getValue('product_id')} />;
    }
  }

 

 

Optimisitic Update

 

[프로젝트] Next.js 장바구니 구현하기 - (3)

수량을 누르면 총 결제금액이 오르는 것까지 구현했다. 근데 수량 버튼을 눌렀을 때 좀 느리게 브라우저에 반영되어서 optimsitic update로 구현해보려고한다.  [프로젝트] Next.js 장바구니 구현하기

ejunyang.tistory.com

 

기존에 있었던 onSuccess는 지우고 onMutate, onError, onSettled 조합을 추가했다. 이미 만들어 놓은 카운터 컴포넌트에서 그대로 가져와서 사용했고, 삭제하는 로직만 변경했다.

onMutate: async (productId: string) => {
      await queryClient.cancelQueries({
        queryKey: ['cart']
      }); // 기존 쿼리 취소

      const previousCart = queryClient.getQueryData(['cart']); // 기존 데이터 저장

      // optimistic update
      queryClient.setQueryData(['cart'], (oldData: CartItem[] = []) => {
        return oldData.filter((item) => item.product_id !== productId);
      });

      return { previousCart }; // 롤백을 위한 이전 데이터 반환
    },

 

 

 

 

프로젝트 적용

 

+ Recent posts