React 6분 읽기

useState

함수 컴포넌트에서 상태를 다룰 때 사용하는 훅은 useState입니다. 그냥 변수를 사용하면 되는 것을 왜 state를 사용해야할까요?

함수 컴포넌트에서 상태를 다룰 때 사용하는 훅은 useState입니다. 그냥 변수를 사용하면 되는 것을 왜 state를 사용해야할까요?

일반 변수 vs state 변수

export default function App() {
  let testCount = 0;
  const [count, setCount] = useState(0);
  // setCount 상태에 새로운 값을 넣어주고 => 값이 변경되었으니까 이걸 반영해서 렌더링을 다시해야된다.
  // setCount 알아서 재 렌더링을 한다.
  return (
    <div className="App">
      {testCount}
      <br />
      {count}
      <br />
      <button
        onClick={() => {
          testCount += 1;
          console.log(testCount);
        }}
      >
        testCount
      </button>
      <button
        onClick={() => {
          setCount((prev) => prev + 1);
          console.log(count);
        }}
      >
        count
      </button>
    </div>
  );
}

위의 코드를 보게 되면 버튼을 눌렀을 때 각각 일반 변수와 state변수의 값을 1 증가시키는 이벤트가 달려있습니다.

실제로 눌렀을 때 값이 변화하는지 확인해봅시다. 콘솔창을 열어서 보면 됩니다.

위 사진은 버튼을 각 한 번씩 눌렀을 때 결과입니다.

브라우저 창을 보시면 위의 숫자가 testCount이고 아래가 count 값입니다.

리렌더링 차이

그런데 뭔가 이상합니다. testcount의 값은 0인데 count의 값은 1로 변경이 되었네요. 하지만 출력된 콘솔창을 보면 testcount의 값이 증가한 것을 볼 수 있죠. 즉, 값은 변경이 되었는데 화면에 반영이 되지 않았다는 것이죠.

이는 자바스크립트로 화면에 어떤 변화하는 데이터를 보여줄 때도 같습니다. 값이 변하면, 그 변한 값을 다시 렌더링해서 반영해야하죠. 하지만, 리액트에서 훅을 이용해 상태를 만들면 그 값이 상태 변경 함수를 통해 변경되었을 때 알아서 리렌더링이 됩니다. 그렇기 때문에 위의 결과가 나타난 것이죠.

변수 값 유지

그렇다면 단순히 리렌더링의 차이만으로 상태를 사용해야하는 것일까요? 아래의 사진을 또 봅시다.

아래의 사진은 testcount의 값을 충분히 증가시키고 count버튼을 눌러 리렌더링을 한 것입니다. 분명 일반 변수는 값이 변하고 리렌더링이 되지 않아 문제였는데, 그러면 상태를 변경했을 때는 리렌더링이 되니까 count 버튼을 눌렀을 때 testcount값이 반영이 되어야 하는 것이 아닐까요? 왜 반영이 되지 않을까요?

그 이유는 함수 컴포넌트가 리렌더링이 될 때 그 함수 자체가 다시 실행되기 때문입니다. 그렇기 때문에 일반 변수가 다시 선언이 되어 초기값을 가지고 있게 되는 것이죠. 하지만 상태 변수는 그 값이 유지되기 때문에 리렌더링이 되어도 화면에 잘 반영이 되는 것이죠.

동기 vs 비동기

또 이상한 점이 있습니다. 첫번째 사진을 다시 봐보죠. 위의 경우는 버튼을 각각 한 번씩 누른 후 값을 콘솔에 출력해주고 있습니다.

<button
  onClick={() => {
    testCount += 1;
    console.log(testCount);
  }}
  >
  testCount
</button>
<button
  onClick={() => {
    setCount((prev) => prev + 1);
    console.log(count);
  }}
  >

위 코드에서 확인할 수 있습니다. 그런데 왜 count는 증가된 값이 보이지 않을까요?? 그 이유는 setCount가 비동기로 동작하기 때문입니다. 비동기에 대한 설명은 제 포스팅에 있으니 한 번 참고하시면 될 것이고, 비동기로 동작하기 때문에 이전 상태 값이 출력이 되는 것이라고 생각하면 됩니다.

그렇다면 왜 이 현상을 알아야 할까요?

<button
  onClick={() => {
    setCount((prev) => prev + 1);
    setAr((prev) => [...prev, count]);
    console.log("count", count);
  }}
  >
  count
</button>
<ul>
  {ar.map((a) => (
    <li>{a}</li>
  ))}
</ul>

위 코드는 count가 증가하면 그 값을 증가한 값을 ar에 넣어주고 화면에 보여주는 방식으로 구현 했습니다.

하지만 버튼을 눌렀을 때 증가한 값이 아닌 이전 상태의 값이 추가가 되네요. 이렇게 비동기로 동작하는 문제를 만나게 되면 우리가 원하는 상태 값을 가지고 오지 못하는 경우가 생길 수 있습니다.

이렇게 비동기적으로 변하는 상태 값을 내가 원하는 시점에 원하는 값을 가져오게 하기 위해서 사용할 수 있는 방법은 위의 상태 변경 함수에 함수를 인자로 넣어 사용하는 방법이 있고, 또 useEffect를 이용해서 처리하는 방법이 있습니다.

이에 대해서는 다음 포스트에서 설명하도록 하겠습니다.