React는 왜 불변성을 지켜야할까?
React는 왜 불변성을 지켜야 하는지를 알아보기 전에 다시 한번 불변성에 대한 개념을 보고 가자!
불변성
저번 불변값 가변값 글에서 불변성은 변하지 않는 바뀔 수 없는 값이라는 것을 알았다.
이 값은 메모리 영역 안에서 변경이 불가능하며 변수에 할당할 때 새로운 값이 만들어진다.
그렇다면 우리는 왜 불변성을 지켜야 할까?
불변성을 지켜야하는 이유
- 값의 변화를 추적하기 쉬움
- 원본데이터가 변경되면, 해당 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생함
불변성을 지키는 방법
1. object 메서드 사용
Object.assign()을 사용하여 데이터를 복사
예시코드
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const answer = Object.assign(target, source);
console.log(answer); // Object { a: 1, b: 4, c: 5 }
Object.freeze()을 사용하여 원시값을 얼림
const obj = {
prop: 1,
};
Object.freeze(obj);
obj.prop = 3;
// Throws an error in strict mode
console.log(obj.prop);
// 1
Object.seal()를 이용하여 객체를 밀봉함
밀봉하게 되면 객체에 새로운 속성을 추가할 수 없고 삭제할수도 없음.
const object1 = {
property1: 42,
};
Object.seal(object1);
object1.property1 = 33;
console.log(object1.property1);
// Expected output: 33
delete object1.property1; // Cannot delete when sealed
console.log(object1.property1);
// Expected output: 33
2. 스프레드 문법
- 스프레드 문법을 이용하여 불변성을 유지할 수 있음
- 하지만 객체의 깊이가 깊어지면 불변성을 유지하기가 힘들다.
- why? 만약 중첩된 객체가 있다면 중첩된 모든 객체에 개별적으로 스프레드 연산자를 적용해줘야 하기 때문이다.
const ex = {
name: 'duck',
age: 26,
};
const new_ex = { ...ex };
ex.name = 'dev';
console.log(ex.name, new_ex.name); // 'dev' 'duck'
3. immer/immutable 라이브러리 사용
4. map, filter, slice, reduce와 같은 새로운 배열을 반환하는 메서드를 사용
그렇다면 왜 REACT에서 불변성을 지켜야하는가?
React는 상태값을 업데이트 할 때 얕은 비교를 수행한다.
만약 불변성을 지키지 않고 객체나 배열을 직접 변경하게 된다면, Virtual Dom이 Props의 변화를 감지하지 못한다.
React에서 불변성을 지키는 간단한 예시
불변성을 지키지 못한 코드
const [person, setPerson] = useState({
name: 'duck',
age: 26,
});
return (
<div>
<span>{person.name}</span>
<span>{person.age}</span>
<button
onClick={() => {
person.age = 25;
setPerson(person);
console.log(person.age);
// 콘솔에는 25 찍히지만 같은 주소를 참조해 리렌더링은 일어나지 않는다.
}}
>
25살로 젊어지게 하는 버튼
</button>
</div>
);
불변성을 지킨 코드
const [person, setPerson] = useState({
name: 'duck',
age: 26,
});
return (
<div>
<span>{person.name}</span>
<span>{person.age}</span>
<button
onClick={() => {
setPerson((prev) => {
return { ...prev, age: 25 };
// 스프레드 연산자를 이용하여 불변성 유지
});
}}
>
25살로 젊어지게 하는 버튼
</button>
</div>
);
리덕스를 사용해본 사람들은 알겠지만,
리덕스를 이용하여 상태관리를 할 때도 불변성을 지켜야한다.
리덕스에서 불변성을 지킨 코드
const initialState = {
name:'duck',
job:'front-dev',
age:null;
}
const reducer = (state=initialState, action) =>{
switch (action.type) {
case TEST:
return {
...state, // 스프레드 연산자를 이용한다.
age:action.data
};
default:
return state
}
}
하지만 위에서 말한 것처럼 스프레드 연산자를 이용하여 불변성을 지킬 때, 객체의 깊이가 복잡해지면 어렵기 때문에, immer 라이브러리를 이용하자..!