IT기타

(React) 버튼 2번 눌리는 현상

emilyyoo 2025. 2. 20. 12:41
728x90

현상 : 

React 로 장바구니에 주문상품을 담는 이벤트를 구현했다.

 

 

그런데 이때 같은 상품을 여러개 담기위해 장바구니에 담는 버튼을 연속으로 눌렀는데

 

한번 누를 때마다 기존수량에 +2 씩 눌리는 현상이 나타났다. 

 

 

**문제의 코드

//장바구니 내역 데이터는 아래와 같은 식이다. 

const prevOrder = [ { id: 1, name: "커피", quantity: 1, options=[] }, { id: 2, name: "티", quantity: 2 ,options=[] } ]; 

 

const addToOrder = useCallback((item: MenuItem, selectedOptionIds: string[]) => {
    const selectedOptionObjects = item.options?.filter((option) => selectedOptionIds.includes(option.id)) || []

    setOrder((prevOrder) => {
      const existingItemIndex = prevOrder.findIndex(
        (orderItem) =>
          orderItem.id === item.id &&
          JSON.stringify(orderItem.selectedOptions) === JSON.stringify(selectedOptionObjects),
      )

      if (existingItemIndex !== -1) {
        // 기존 항목이 있으면 수량을 1 증가
        return prevOrder.map((orderItem, index) =>
          index === existingItemIndex ? { ...orderItem, quantity: orderItem.quantity + 1 } : orderItem,
        )
      } else {
        // 새 항목 추가
        return [...prevOrder, { ...item, quantity: 1, selectedOptions: selectedOptionObjects, note: "" }]
      }
    })

    setSelectedOptions((prev) => ({ ...prev, [item.id]: [] }))
  }, [])

 

 

**작동에러 원인 분석

1. 주요 문제: setOrder()의 비동기 업데이트로 인한 상태 불일치.

 

 위의 코드 중 기존 항목의 수량을 증가시킬 때 다음과 같은 로직을 사용했다.

```javascript
if (existingItemIndex !== -1) {
  return prevOrder.map((orderItem, index) =>
    index === existingItemIndex
      ? { ...orderItem, quantity: orderItem.quantity + 1 }
      : orderItem
  )
}
```

이 로직은 겉보기에는 정상적으로 보이지만, React의 상태 업데이트 방식과 관련하여 문제가 있었다.

 

 

2. 문제의 세부 원인:

a. 상태 업데이트의 비동기성React에서 상태 업데이트는 비동기적으로 일어난다. 즉, `setOrder` 함수를 호출한 직후에 상태가 즉시 업데이트되지 않는다.

b. 클로저(Closure)와 이전 상태: `addToOrder` 함수가 호출될 때마다, 이 함수는 호출 시점의 `prevOrder` 상태를 참조한다. 빠르게 연속해서 함수를 호출하면, 각 호출이 동일한 이전 상태를 기반으로 실행될 수 있다.


3. 문제의 결과:
위의 요인들로 인해, 사용자가 빠르게 여러 번 "주문 추가" 버튼을 클릭하면, 각 클릭이 이전 상태를 기반으로 수량을 1 증가시키려 했지만, 실제로는 마지막 업데이트만 적용되어 수량이 2씩 증가하는 것처럼 보였다.


4. 해결 방법:
수정된 코드에서는 


```javascript
if (existingItemIndex !== -1) {
  return prevOrder.map((orderItem, index) =>
    index === existingItemIndex 
      ? { ...orderItem, quantity: orderItem.quantity + 1 } 
      : orderItem
  )
} else {
  return [...prevOrder, { ...item, quantity: 1, selectedOptions: selectedOptionObjects, note: "" }]
}
```

이 접근 방식은 다음과 같은 이유로 문제를 해결할 수 있다.:

- 매번 새로운 배열을 생성하여 반환함으로써, React가 상태 변화를 확실히 감지하고 컴포넌트를 다시 렌더링하도록 했다.
- 각 업데이트가 이전 상태를 정확히 반영하도록 보장한다.
- 연속된 클릭에 대해 각각의 상태 업데이트가 독립적으로 처리되도록 했다.

728x90