시작하기에 앞서 이 글은 개발을 공부하며 새로 알게된 내용을 제 나름의 해석대로 정리한 글입니다. 잘못된 부분이 있다면 저의 올바른 지식향상을 위해 지적해주시기 바라겠습니다.
혹시 react를 사용해서 어플리케이션을 만드는데 useState를 사용해서 state값을 업데이트를 해줬는데도 리렌더링되지 않는 문제가 발생한다면 혹시 setState를 하는 부분에서 위의 코드와 같은 형태로 하지 않았는지 확인해보시기 바라겠습니다.
위의 코드의 의도는 버튼을 누르게 되면 count가 1씩 증가해 브라우저에서 count값이 1씩 증가햐며 리렌더링되게 하는 것입니다. 하지만 실제로 실행시켜서 버튼을 눌러보면 리렌더링이 일어나지 않습니다. 이것은 위의 코드에서 불변성을 지키지 않았기 때문입니다. 불변성을 지키지 않은 부분은 아래와 같습니다.
위처럼 간단한 코드를 짤 때는 사실 주로 저런 식으로 코드를 짜는 경우가 거의 없기 때문에 이런 리렌더링이 되지 않는 문제를 겪는 경우가 적지만 좀 더 복잡한 앱을 만들고 state가 많아지면서 위의 코드와 같은 형식으로 코드를 짜는 경우가 생깁니다. 저의 경우도 저번 react를 활용한 프로젝트를 진행하면서 코드를 불변성을 지키지 않고 짜서 리렌더링 되지 않는 문제때문에 오랜시간 골머리를 앓으며 씨름한 경험이 있습니다. 그럼 도대체 불변성이 뭐고 왜 저런식으로 코드를 짜면 리렌더링이 되지 않는 문제가 발생하는 것일까요?
우선 불변성을 지킨다는 것이란 어떤 값을 변경할 때 그 값을 직접적으로 변경하지 않고 새로운 값을 새로 만들어서 변경하는 것을 의미합니다. 예시를 보여드리자면 아래의 코드는 불변성을 지킨 코드라고 할 수 있습니다.
이전에 나온 코드랑의 차이점이라고 하면 이전의 코드에서는 counter 객체의 참조값을 temp라는 변수에 저장하고 그 참조값을 통해 count를 변경시킨 후 setCounter에 temp를 넣어준 것이라면 이 코드는 spread를 사용해 counter의 얕은복사를 통해 새로운 객체를 만들어서 setCounter의 인자로 넣어준 것입니다.
전자의 경우 counter를 temp에 넣어줬다고는 하나 참조값을 대입한 것이기 때문에 사실상 값의 변경에 있어 그 값을 직접적으로 변경한 것입니다. 하지만 후자의 경우는 이전의 값을 복사해 새로운 값을 만들어서 counter의 state를 변경시켜준 것이라고 할 수 있죠. 그럼 왜 전자의 방법으로 하면 리렌더링이 일어나지 않는 것일까요?
React에서 리렌더링이 일어나는 조건은 크게 3가지가 있습니다.
- props가 바뀔 때
- state값이 바뀔 때
- 부모 컴포넌트가 리렌더링 될 때
여기서 state값이 변경되었는지는 react 내부적으로 비교를 통해 판단을 하게 되는데요, 여기서 react는 얕은복사를 사용해 비교합니다. 이 말이 무슨 말이냐, 객체의 경우 객체 안의 값들이 바뀌었는지 일일히 다 확인을 하여 값의 변경여부를 판단하는 것이 아니라 객체가 참조하고 있는 값이 같냐를 확인한다는 것입니다. 그러니깐 처음의 코드처럼 불변성을 지켜주지 않는다면 아무리 state가 바뀌어도 그 state 객체를 참조하는 참조값은 바뀌지 않기 때문에 react에서는 state가 바뀌었다고 인지하지 못하고 리렌더링을 하지 않는 것입니다.
그럼 react는 왜 얕은 비교로 state 변경 여부를 판단하는 것일까요? 비교를 할 때 값들을 하나하나 비교하는 것은 너무 많은 연산을 요구하게 됩니다. 만약 객체 안에 또 다른 객체 참조값이 있고 그 안에 또 객체 참조값이 있다면 계속 객체 안의 값들에 대해 하나하나 비교를 해야하는 경우가 생길 수 있는데 이 경우 성능이 나빠질 수 있기 때문에 react는 성능 최적화를 위해 이 방법을 사용합니다.
그래서 결론은 react는 이런 식으로 만들어져 있으니깐 원치않는 에러를 방지하기 위해서는 불변성을 꼭 지켜서 코드를 짜시기 바라겠습니다!
감사합니다!