23년 5월 3일 흑우집합소 개발노트 (React에서 useState의 상태 처리)

Posted by , May 04, 2023
TILReactJS흑우집합소
Series ofTIL

thumbnail

React에서 상태 관련 처리를 하다가...

신규 기능 생성번호 당첨내역을 개발하고 있었다.
백엔드 로직 개발을 하고, 프론트쪽 개발로 넘어왔다.

각 로또 등위별 당첨 내역 처리를 위해 아래와 같은 상태 값을 사용했다.

const [rankInfo, setRankInfo] = useState<RankSummary>({
  firstRankCnt: 0,
  secondRankCnt: 0,
  thirdRankCnt: 0,
  fourthRankCnt: 0,
  fifthRankCnt: 0,
})

위 상태 값은 아래의 등위별 당첨 내역의 카운트에 쓰인다.

img01


저 등위별 당첨 내역은 컴포넌트로 따서 구현되어 있다.
컴포넌트에서는 useEffect hook을 이용해서 초기 값을 설정해준다.

useEffect(() => {
  const makeLogData = winData.lottoMakeLogData

  setRankInfo({
    firstRankCnt: 0,
    secondRankCnt: 0,
    thirdRankCnt: 0,
    fourthRankCnt: 0,
    fifthRankCnt: 0,
  })

  makeLogData.forEach(item => {
    if (item.rank === 5) {
      setRankInfo({
        ...rankInfo,
        ...{ fifthRankCnt: rankInfo.fifthRankCnt++ },
      })
    } else if (item.rank === 4) {
      setRankInfo({
        ...rankInfo,
        ...{ fourthRankCnt: rankInfo.fourthRankCnt++ },
      })
    } else if (item.rank === 3) {
      setRankInfo({
        ...rankInfo,
        ...{ thirdRankCnt: rankInfo.thirdRankCnt++ },
      })
    } else if (item.rank === 2) {
      setRankInfo({
        ...rankInfo,
        ...{ secondRankCnt: rankInfo.secondRankCnt++ },
      })
    } else if (item.rank === 1) {
      setRankInfo({
        ...rankInfo,
        ...{ firstRankCnt: rankInfo.firstRankCnt++ },
      })
    }
  })

  setRankInfo(updatedRankInfo)
}, [winData])

근데 이렇게 했더니 계속 값이 증분했다.
앞에 초기화 로직을 줬지만 적용되지는 않았다.

왜 그런걸까?

이 부분을 찾아보니...
아래와 같이 정리할 수 있었다.

useState hook은 비동기로 동작하기 때문에 setRankInfo가 호출되어도 rankInfo는 즉각적으로 업데이트 되지 않는다.
그래서 이를 해결하려면 이전상태를 복사한 값을 적용함으로써 해결할 수 있다.

말이 좀 어려운데 아래 솔루션 코드를 보면 알 수 있다.

useEffect(() => {
  const makeLogData = winData.lottoMakeLogData

  // 새로운 객체를 생성하여 상태 업데이트를 수행합니다.
  const updatedRankInfo = {
    firstRankCnt: 0,
    secondRankCnt: 0,
    thirdRankCnt: 0,
    fourthRankCnt: 0,
    fifthRankCnt: 0,
  }

  makeLogData.forEach(item => {
    if (item.rank === 5) {
      updatedRankInfo.fifthRankCnt = updatedRankInfo.fifthRankCnt + 1
    } else if (item.rank === 4) {
      updatedRankInfo.fourthRankCnt = updatedRankInfo.fourthRankCnt + 1
    } else if (item.rank === 3) {
      updatedRankInfo.thirdRankCnt = updatedRankInfo.thirdRankCnt + 1
    } else if (item.rank === 2) {
      updatedRankInfo.secondRankCnt = updatedRankInfo.secondRankCnt + 1
    } else if (item.rank === 1) {
      updatedRankInfo.firstRankCnt = updatedRankInfo.firstRankCnt + 1
    }
  })

  setRankInfo(updatedRankInfo)
}, [winData])

위와 같이 새로운 객체를 주고 해당 값으로 증가를 해준 다음 해당 값으로 setRankInfo를 보내주면 해결이 된다.


정리

useState, useEffect 함수는 비동기임을 잘 알고 써야할 것 같다.
그나저나 백엔드, 프론트엔드 둘다 왔다갔다 하니 약간 햇갈리기도 한다.

일할때는 역시 정신 똑바로 차리고 해야 할듯...