본문 바로가기

react

[React] 리엑트에서의 불변성과 immer 라이브러리

리엑트에서의 불변성


불변성의 의미

불변성이란, 메모리에 할당된 값이 변하지 않음을 의미한다.

예컨대, 자바스크립트에서 원시값은 불변성을 가지는데, 이는 변수에 재할당을 하는 경우 기존 데이터는 변하지 않으며 새로운 메모리에 데이터를 할당하고 변수는 새 메모리를 가리키기 때문이다.

반면, 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이지만 성능적으로는 안 사용하는 것이 빠르긴 하다. 물론 이 차이는 그리 크지 않다. 하지만, ImmerJavaScript 엔진의 Proxy 라는 기능을 사용하는데, 구형 브라우저 및 react-native 같은 환경에서는 지원되지 않아 ES5 fallback 을 대신 사용한다. 이 때는 꽤나 느려질 수 있으니 주의해야 한다.

 

그러니, state는 가능하면 구조를 간단하게 관리하고, 불가피한 경우에만 사용하는 게 좋다.