[트러블슈팅] useEffect 사용 시 무한루프가 발생하는 문제

지혜·2024년 7월 12일
0

1. useEffect의 종속성 배열에 객체를 넣으면 객체의 참조 변화 여부에 따라 무한 루프 문제가 발생

구현 시나리오

  1. 로컬스토리지에 저장된 cart 데이터로 db에 저장된 product 데이터를 가져오기
    useCartProducts hook 사용
  2. 가져온 cartProducts 데이터 중에서 수량이 0인 상품을 제외하고 validProducts에 저장
    → hook에서 데이터 필터링을 하지 않는 이유 : 리스트에서 품절된 상품도 보여주되, 선택 및 구매는 불가능하게 끔 하기 위해
  3. validProducts 데이터를 selectedItems 에 저장해서 페이지에 접근했을때 상품이 선택된 상태로 보여줌
    useSelection : 리스트를 전달하면, 아이템의 선택 관련 이벤트를 처리해주는 hook

발생한 문제점

useEffect에서 cartProductsvalidProducts 와 데이터가 변경되면 상태를 업데이트 하도록 처리하는 과정에서 무한루프가 발생함

코드

// CartList.tsx
****
const { cart, removeSelectCart, toggleCart, updateCartIsBuy } = useCart();
const { cartProducts, isError } = useCartProducts(cart);

const [validProducts, setValidProducts] = useState<(Cart & Product)[]>([...cartProducts.filter((item) => item.productQuantity !== 0)]);

const { selectedItems, setSelectedItems, toggleItemSelection, toggleAllItemSelection } = useSelection(validProducts);

useEffect(() => {
  setValidProducts([...cartProducts.filter((item) => item.productQuantity !== 0)]);
}, [cartProducts])

useEffect(() => {
  setSelectedItems(validProducts.map((item) => item.id))
}, [validProducts])

해결

원인 : cartProducts 배열이 변경될 때마다 useEffect가 실행되는데 cartProducts.filter() 메서드를 호출하면 항상 새로운 배열이 생성되기 때문에 이 배열의 참조값이 변경되어 무한 루프가 발생

해결 : 의존성 배열을 cartProducts 배열 자체가 아니라 cartProducts.cartProducts로 변경해 배열의 길이가 변경될 때만 감지하도록 함

useEffect(() => {
  const validItems = cartProducts.filter((item) => item.productQuantity !== 0);
  setValidProducts(validItems);
}, **[cartProducts.length]**);

2. useEffect의 종속성 배열에 객체를 넣으면 무한 루프 문제가 발생

구현 시나리오

tosspayments로 결제를 완료 하면 successUrl로 지정한 페이지로 이동하게 된다.

결제 성공 화면에서 처리할 작업

  1. Cart에 담긴 isBuy 상태의 상품 리스트를 buyProducts 저장
  2. Params로 전달받은 결제 정보를 payment 저장
  3. mutateAddOrder에 전달해 주문 정보 저장

발생한 문제점

mutateAddOrder를 호출하는 useEffect에서 무한 루프 발생

코드

// PaymentSuccess.tsx
const { cart } = useCart();
const buyProducts = (item) => item.isBuy);

const { user } = useAuth();
const [searchParams] = useSearchParams();

const userId = user?.uid;
const orderId = searchParams.get('orderId');
const amount = Number(searchParams.get('amount'));

if (!userId || !orderId || !amount) return

const payment = { userId, orderId, amount }

const { mutate: mutateAddOrder } = useAddOrder();

useEffect(() => {
  if (!payment || !buyProducts.length) return;

  const processOrder = async () => {
    await mutateAddOrder({ payment, buyProducts });
  };

  processOrder();
}, [payment, buyProducts, mutateAddOrder]);

해결

원인 : useEffec의 종속성 배열에 객체를 넣으면 무한 루프 문제가 발생

얕은 비교를 통해 참조 값이 변경되었는지 확인하는데 렌더링할 때마다 객체의 참조 값이 변경되므로 useEffect가 다시 실행됨

해결 : useMemo를 사용해서 렌더링 중에 상태의 참조 값이 변경되지 않도록 함

const { cart } = useCart();
**const buyProducts = useMemo(() => cart.filter((item) => item.isBuy), [cart]);**

const { user } = useAuth();
const [searchParams] = useSearchParams();

const userId = user?.uid;
const orderId = searchParams.get('orderId');
const amount = Number(searchParams.get('amount'));

**const payment = useMemo(() => {
  return userId && orderId && amount ? { userId, orderId, amount } : null;
}, [userId, orderId, amount]);**

const { mutate: mutateAddOrder } = useAddOrder();

useEffect(() => {
  if (!payment || !buyProducts.length) return;

  const processOrder = async () => {
    await mutateAddOrder({ payment, buyProducts });
  };

  processOrder();
}, [payment, buyProducts, mutateAddOrder]);

참고
https://ko.react.dev/learn/lifecycle-of-reactive-effects
https://velog.io/@summereuna/리액트-useEffect-무한-루프-탈출하기

0개의 댓글