💥
React는 왜 불변성을 유지해야되나?
 December 22, 2022
리액트의 상태 업데이트는 항상 불변적으로 수행되어야 한다.
- mutate(돌연변이한)한 값의 대상과 위치에 따라 컴포넌트가 렌더링 되지 않을 수 있다.
- 데이터가 실제로 업데이트 된 시기와 이유에 대해 혼란을 겪을 수 있다.
불변성이란?
- Immutability(불변성)는 상태나 값을 변경하지 않는 것이다.
- JavaScript에서 예를 들자면,
- JavaScript의 원시 타입의 값들은 불변성을 갖고 있다.- 데이터 타입
- 원시 타입(string, number, bigint, boolean, undefined, ES6 부터 추가된 symbol)
- 변경이 불가능한 값(읽기전용)
- 원시 값을 변수에 할당하면 확보된 메모리 공간(주소)에는 실제 값이 저장됨
- 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시값이 복사되어 전달된다.
 
- 참조 타입(Object, Array, Function)
- 변경이 가능한 값
- 객체를 변수에 할당하면 확보된 메모리 공간에 참조값이 저장됨
- 객체를 가르키는 변수를 다른 변수에 할당하면 원본의 참조값이 복사되어 전달된다.
 
 
- 원시 타입(string, number, bigint, boolean, undefined, ES6 부터 추가된 symbol)
- 즉, 원시타입은 재할당을 통해서만 변수에 저장한 값을 변경할 수 있지만, 객체타입은 재할당 없이 객체를 직접 동적으로 추가, 삭제 갱신 등이 가능하다는 것이 큰 차이점이다.
 
- 데이터 타입
 
- JavaScript의 원시 타입의 값들은 
추가로 JavaScript에서 얕은 복사와 깊은 복사
- 얕은 복사(Shallow copy)
- 객체를 프로퍼티 값으로 갖는 객체의 경우에 한 단계 까지만 복사하는 것을 말한다.
- 즉, 첫 객체는 새로운 메모리 주소를 가지지만 내부에 있는 중첩된 객체는 얕은 복사를 한 경우에 같은 메모리 주소를 가진다는 의미가 된다.
- 얕은 복사 방법으로는 Object.assign(),전개연산자(spread)
- 얕은 복사는 하위 중첩되어 있는 객체까지 복사하지 않으므로 공유에 의한 예기치 못한 결과를 얻을 수 있다.
 
- 깊은 복사(Deep copy)
- 하위에 중첩되어 있는 객체(배열 등)까지 모두 복사하는 것을 말함
- 즉, 원시 값처럼 완전히 새로운 메모리 공간을 차지하는 원시값처럼 복사본을 만든다.
- 깊은 복사 방법으로는 JSON.stringify(),재귀함수,lodash의 cloneDeep
 
- 결론으로 객체의 참조 특성으로 인해 복사하고자 하는 객체에 중첩된 객체타입이 있는지를 확인하고 그에 맞는 복사 방법을 사용해야 한다.
그래서 리액트 상태를 변경할 때 불변성을 지켜주어야 되는 이유?
- 리액트에서 불변성을 지켜주어야 되는 이유는 리액트가 상태 업데이트를 하는 원리 때문이다.
- 리액트는 상태 값을 업데이트 할 때 얕은 비교로 상태 변경 여부를 체크하며, 수행한다.
- 즉, 리액트는객체의 속성 하나하나를 비교하는게 아니라참조 값만 비교하여 상태 변화를 감지한다.
- 이런 이유로 리엑트는 state(배열이나 객체)를 업데이트 할 때 setState()를 사용하여배열이나객체를 새로 생성해 새로운 참조 값을 만들어상태를 업데이트한다.
- 그리고 리엑트에서 불변성을 지켜주면 다른 이점으로 사이드 이펙트를 방지할 수 있다.
정리하자면
리액트는 불변성을 지켜주면 효과적인 상태 업데이트(얕은 비교 수행: 계산 리소스를 줄여줌)와 사이드 이펙트를 방지하는 이점(원본 데이터를 건들게 될 경우, 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있으며, 프로그래밍의 복잡도가 올라간다.)을 가질 수 있게 된다.
불변성을 지키며 상태를 업데이트 하는 방법은?
- 
spread operator, map, filter, slice, reduce 등등 새로운 배열을 반환하는 메소드들을 활용하거나- splice는 원본데이터를 변경하기 때문에 사용하면 안된다.
 
- 
setState를 이용할 경우- 원시타입인 경우에는 값을 바로 넣어서 사용해도 되지만
- 참조타입인 경우에는 새로운 객체나 배열을 생성한 후 값을 넣어주어야 한다.
 // 원시타입 const [number, setNumber] = useState(0); setState(7); // 참조타입 const [person, setPerson] = useState({ name: '', age: 33 }); setState({ ...person, name: 'seahyun' });
정리
- 불변성이란 메모리 영역의 값을 변경하지 않는 것이다.
- 리액트는 불변성을 지켜줌으로써 효율적인 상태업데이트를 한다.
- 리액트는 불변성을 지켜줌으로써 사이드 이펙트를 사전 방지하고 프로그래밍의 구조를 단순하게 유지한다.
- 불변성을 가진 원시타입과 달리 참조타입의 경우에는 의도적으로 불변성을 지켜주어야한다.- 방법으로는새로운 주소 값을 가진 객체(배열)를 생성하여 상태를 업데이트 해주어야 한다. 또는 spread operator, map, filter, slice, reduce 메소드들을 이용하여 새로운 배열의 값을 반환하여 사용한다.