들어가는 말)
react 라는 주제로 면접 스터디를 진행하였습니다. 이때 리엑트의 불변성과 관련된 질문이 많이 언급되었습니다.
이번 기회에 불변성이 어떤 성질이며 React에서 왜 불변성을 유지해야하는지 살펴보려 합니다.
불변성이란 말 그대로 변하지 않는 성질이라는 뜻을 가지고 있습니다.
그러나 이것이 왜 react에서 중요하지? 라는 생각이 가장 먼저 들었습니다.
이는 JS의 원시타입을 통해 조금 더 설명해보겠습니다.
원시타입은 불변성을 가지고 있습니다. 그럼 값을 변경할 수 없다는 뜻으로 이해하기 쉬운데요
let string_sample = ‘test1’
string_sample = ‘test2’
위에서는 string_sample가 test1에서 test2로 변경되었습니다. 그러나 이는 그렇게 보이는 것입니다.
이는 string_sample변수가 test1에서 test2로 변경된 것 처럼 보이지만 실제 메모리 영역에는 test1, test2가 모두 존재합니다.
즉 string_sample: ‘test'이 메모리 1영역에 등록이 되고
string_sample: ‘test2’가 메모리 2영역에 새로 할당하게 됩니다.
즉 메모리 영역이 대체되는 것이아닌 새로 할당이 되는 것입니다. 그리고 이것글 불변성이라고 합니다.
다음은 참조 타입에 대한 불변성을 살펴보겠습니다.
let array_sample = [0,1,2,3,4] //메모리 영역 1
array_sample.push(5) //메모리 영역 1
array_sample = [0,1,2,3,4] //메모리 영역 2
위 array_sample에 push(5)를 함으로 원본 배열을 수정하는 즉 불변성을 지키지 않고 있습니다.
그리고 array_sample = [0,1,2,3,4] 위 문장은 원본 배열을 수정하는 것이 아니라 새 참조 값을 가진 새로운 배열을 할당하여 불변성을 지킵니다.
즉 불변성이란 메모리 영역에 할당된 값이 변하지 않는다고 말할 수 있습니다.
위 파트에서 불변성이란 무엇인지를 살펴보았습니다. 그렇다면 리엑트에서는 이런 불변성을 어떤 이유에 지켜야하는 것일까요?
이는 리엑트의 상태 업데이트의 원리에 답이 있습니다.
리엑트는 상태값을 업데이트 할 때 얕은 비교를 수행합니다. 이는 객체의 속성을 하나하나 비교하는 것이 아닌 이전 참조값과 현재 참조값만을 비교하여 상태 변화를 감지하게 됩니다.
이런 이유로 배열이나 객체를 업데이트 할 때 setState([…state, newState]) 등을 사용하여 새로운 참조값을 가진 배열이나 객체를 생성하는 방식을 사용합니다.
state의 불변성을 지키지 않는 즉 setState를 사용하여 state 값을 할당하는 것이 아닌 직접적으로 state값을 변경하게 된다면 이는 불변성을 지키지 않게 됩니다.
이렇게 직접적인 state의 수정은 새로운 참조값을 가지지 않기에 리액트가 state 값이 변경되었다는 것을 알 수 없어 상태변화를 감지하고 반영할 수 없습니다.
상태 변화와 함께 불변성을 지키게 된다면 사이트 이펙트를 방지할 수 있습니다.
외부에 존재하는 원본데이터를 직접 수정하지 않고, 원본 데이터를 복사하여 사용하기에 예상하지 못한 오류를 사전에 방지할 수 있습니다. 이는 다시 생각해보면 외부의 값을 함부로 변경할 수 있다는 것은 꽤나 위험한 일일 것입니다. 만약 다른 어딘가에서 원본 데이터를 사용하고 있는 파트가 있는데 이 값을 변경한다면 이는 사이드 이펙트가 일어날 가능성이 농후하기 때문입니다.
결론적으로 리액트는 불변성을 지킴으로 효과적인 상태 업데이트와 사이드 이펙트를 방지하는 이점을 가질 수 있습니다.