개발 프로젝트/스위트케어

불변성 개념을 적용해 상태 관리 문제 해결하기

티시즌 2024. 1. 8. 23:01

React에서 불변성 이해하기: 상태 업데이트 문제 해결

데이터 검색 기능을 구현하기 위해 검색조건 숫자 범위를 state에 저장하고 서버에 전달하려고 했다.

먼저 해당 값을 저장하는 state를 선언하고,

const [wages, setWages] = useState([9860, 9860]);

다음과 같이 focus out 되면 state를 업데이트하는 코드를 작성했다.

        <div className='input_wrapper'>
          <label>시급</label>
          <div className={styles.input_wrapper}>
            최소
            <input
              type='number'
              value={wages[0]}
              onChange={(e) => handleWageChange(e, 0)}
              onBlur={updateWage}
              min={9860}
              max={1000000}
            />
            원 ~ 최대
            <input
              type='number'
              value={wages[1]}
              onChange={(e) => handleWageChange(e, 1)}
              onBlur={updateWage}
              min={9860}
              max={1000000}
            />
            원
          </div>
        </div>
const updateWage = () => {
  if (wages[0] < 9860) {
    alert('최소 시급은 2024년 기준 최저임금 9,860원 이상이어야 합니다.');
    setWages([9860, wages[1]])
  }

  if (wages[0] > wages[1]) {
    alert('최대 시급은 최소 시급보다 높아야 합니다.');
    setWages([wages[0], wages[0]])
  }

  setCheckedItems({
    ...checkedItems,
    wage: wages,
  });
};

View에서는 정상 작동하는 것으로 보였다.
시급을 0으로 바꾸면 최저시급 미만이라는 오류가 나고, View 상에는 0이 9860으로 변한 것을 볼 수 있었다.
그러나 이때 실제로 서버에 전달되는 값은 0이었다...

동작을 차근히 다시 살펴보았다.
alert가 뜬 다음 조건문을 통해 setWages로 새로운 wages 값을 지정하게 되어 있지만 useState의 상태 변경은 비동기적으로 작용하기 때문에 변동값이 적용되기 전 setCheckedItems가 호출된다.
그리고 value로 wages state를 제공하기 위해서 onChange가 발생할 때마다 state를 변경할 수밖에 없기 때문에, 현재 상태의 state는 입력된 그 값 그대로이다.

위 문제 해결을 위해 다음과 같이 수정했다.

const updateWage = () => {
  if (wages[0] < 9860) {
    alert('최소 시급은 2024년 기준 최저임금 9,860원 이상이어야 합니다.');
    wages[0] = 9860;
  }

  if (wages[0] > wages[1]) {
    alert('최대 시급은 최소 시급보다 높아야 합니다.');
    wages[1] = wages[0];
  }

  setWages(wages);
  setCheckedItems({
    ...checkedItems,
    wage: wages,
  });
};

 

좀더 안정적으로 구동하기 위해 다음과 같이 수정했다.

const updateWage = () => {
  let newWages = [...wages]; // wages 배열을 복사하여 새로운 배열을 생성

  if (newWages[0] < 9860) {
    alert('최소 시급은 2024년 기준 최저임금 9,860원 이상이어야 합니다.');
    newWages[0] = 9860;
  }

  if (newWages[0] > newWages[1]) {
    alert('최대 시급은 최소 시급보다 높아야 합니다.');
    newWages[1] = newWages[0];
  }

  setWages(newWages);  // newWages를 이용하여 한 번에 state를 업데이트

  setCheckedItems({
    ...checkedItems,
    wage: newWages,
  });
};

이렇게 되면, 원본 배열을 직접 수정하는 것이 아니라 복사본을 수정하기 때문에 예기치 않은 사이드 이펙트를 방지할 수 있다.
첫 번째 수정본에서는 함수 실행 시마다 wages state의 원본 배열을 직접 수정한다. 반면 위 코드에서는 wages 배열의 복사본인 newWages를 만들어 사용하므로, 원본 데이터를 보존하면서 안전하게 작동하는 방식이라고 할 수 있다.

결과적으로 프로그래밍의 '불변성(Immutability)' 원칙을 따르게 되어 함수형 프로그래밍의 핵심 원칙을 준수하게 된다.