리엑트에서의 불변성
불변성의 의미
불변성이란, 메모리에 할당된 값이 변하지 않음을 의미한다.
예컨대, 자바스크립트에서 원시값은 불변성을 가지는데, 이는 변수에 재할당을 하는 경우 기존 데이터는 변하지 않으며 새로운 메모리에 데이터를 할당하고 변수는 새 메모리를 가리키기 때문이다.
반면, object에 새로운 프로퍼티를 할당하거나, 배열에 새로운 인덱스를 추가하는 것은 객체가 할당된 메모리의 값을 변경시키므로 불변성을 어긴다.
리엑트에서 state의 불변성을 지키는 이유
setState의 업데이터 함수를 사용할 때 불변성을 지키기 위해 새로운 객체를 리턴해라는 말을 많이 들어봤을 것이다.
const [state, setState] =useState([1,2,3,4])
// 잘못된 방식. 기존 state의 불변성을 파괴함
setState((prev)=>{
prev.push(5)
return prev
})
// 옳은 방식. 기존 state의 불변성 유지
setState((prev)=>{
const a = [...prev]
a.push(5)
return a
})
불변성을 지키는 이유는 크게 두가지가 있다.
- 렌더링 메커니즘 | 리엑트는 상태변화를 감지하기 위해 이전 state와 새 state 간에 얕은 비교를 한다. 따라서, 기존 state의 내부 값을 바꾸는 것으로는 리엑트가 상태변화를 감지하지 못한다.
- 사이드 이펙트 문제 | 원본 데이터를 직접 수정하는 경우, 그것에 의존적인 다른 로직들에 사이드 이펙트가 예상치 못한 방향으로 발생할 수 있다.
- 방안 | 스프레드 연산자를 이용해서 기존 state 객체를 복사 후 변형하면 불변성 문제를 쉽게 해결할 수 있다.
immer 라이브러리를 이용한 불변성 관리
스프레드 연산자 방법은 간단한 state를 업데이트 할 때는 불편함이 별로 없다. 하지만, api의 응답처럼 크고 복잡한 객체를 state에 할당한 후 업데이트할 때는 불변성 때문에 상당한 불편함이 생긴다.
이 때 불변성을 신경쓰지 않으면서 업데이트할 수 있게 해주는 라이브러리가 immer 이다.
방식
import { produce } from "immer";
produce(state, func)
setUsers((prev) => {
const a = produce(prev, (draft) => {
draft.push(newUser);
});
return a;
});
성능
객체의 불변성을 지키는 데 편리함을 주는 immer이지만 성능적으로는 안 사용하는 것이 빠르긴 하다. 물론 이 차이는 그리 크지 않다. 하지만, Immer는 JavaScript 엔진의 Proxy 라는 기능을 사용하는데, 구형 브라우저 및 react-native 같은 환경에서는 지원되지 않아 ES5 fallback 을 대신 사용한다. 이 때는 꽤나 느려질 수 있으니 주의해야 한다.
그러니, state는 가능하면 구조를 간단하게 관리하고, 불가피한 경우에만 사용하는 게 좋다.
'react' 카테고리의 다른 글
[react]리엑트의 비교조정(reconciliation) 알고리즘 이해 (0) | 2024.03.15 |
---|---|
[React] 리엑트 디자인 패턴: 컴포넌트와 합성 컴포넌트 (0) | 2024.03.12 |
[React] 리엑트 살펴보기8: useRef를 사용하는 이유(컴포넌트 사이클에 독립적인 객체 저장, 비제어 컴포넌트) (0) | 2024.03.11 |
[React]리엑트 살펴보기7: context API에 대해 (0) | 2024.03.11 |
[React]리엑트 살펴보기6: useReducer 사용 방법 (0) | 2024.03.10 |