[REACT] State와 불변성

타키탸키·2022년 11월 25일
0

REACT

목록 보기
1/1

React에서 state는 불변성(Immutability)을 유지해야 한다. 불변성이란, 메모리 영역에서 직접적인 변경이 불가능하다는 것을 의미한다. state의 불변성이 유지되었을 때에만 React의 컴포넌트에서 상태가 변경되었음을 감지할 수 있다.

원시 타입 vs. 객체 타입

string, number, boolean과 같은 원시 타입은 기본적으로 불변성을 지니고 있다. 따라서, 값 그대로 저장 혹은 할당되고 복사가 가능하다.

var number = 100;
var score = number;
var number = 40;
console.log(number, score); // 40, 100

위 예시에서 number와 score는 둘 다 숫자 100을 저장한다. 그 다음, number의 값을 40으로 바꾸고 출력하면 각각 40과 100이라는 수가 나온다. 이처럼 원본 변수인 number의 값을 바꾸더라도 원본 변수의 값을 할당 받은 score 변수의 값은 바뀌지 않는다.

이와 달리, 객체는 참조에 의해(by reference) 저장과 복사가 이루어진다. 이 말은 즉, 변수에 객체가 아닌 객체에 대한 참조 값(메모리 주소)이 저장된다는 것을 의미한다.

let goods = { price: 3000 };

let product = goods;

goods.price = 100;

console.log(product.price); // 100

원시 타입의 예제와 같이, 하나의 객체를 생성하고 다른 객체에 직접 대입한 경우를 살펴보자. 이 경우, 원시 객체의 속성 값을 변경하자 원시 객체를 할당 받은 객체의 속성 값도 함께 변하는 것을 볼 수 있다. 이러한 일이 발생하는 이유는 객체가 값이 아닌 메모리 주소를 저장하면서 동일 메모리 주소에 접근하기 때문이다.

이러한 현상이 발생하는 원인에는 얕은 복사와 깊은 복사의 개념이 작용한다.

깊은 복사 vs. 얕은 복사

깊은 복사란, 데이터 자체를 통째로 복사하는 방식을 말한다. 이때, 복사된 두 객체는 완전히 독립적인 메모리를 차지한다. 즉, 인스턴스가 메모리에 새로 생성된다. 모든 값 타입(value type)에는 깊은 복사가 적용되므로 값 타입인 원시 타입은 값 그대로 저장 및 복사가 가능한 것이다.

반대로 얕은 복사는 값 자체가 아닌 주소 값을 대신 복사하며, 인스턴스가 메모리에 새로 생성되지 않는다. 객체와 배열 타입은 이와 같이 참조에 의한 할당이 일어나 두 변수가 같은 주소를 가지게 된다.

객체 타입의 state 갱신

결론적으로, 객체와 배열 타입의 state는 처음부터 불변성을 가진 원시 타입의 state와는 다른 방법으로 업데이트를 진행해야 한다. 만약 원시 타입처럼 데이터를 통째로 복사하는 깊은 복사를 하게 되면, state의 불변성에 어긋남에 따라 컴포넌트 상태가 갱신되지 않는다.

그렇다면 객체 타입의 state를 갱신하는 방법은 무엇일까? 다음 코드를 살펴보자.

const [value, setValue] = useState([1]);
newValue = [...value];
newValue.push(2);
setValue(newValue);

여기서 value 앞의 점 세 개는 spread 연산자로, 이 연산자를 사용하면 원본의 값을 변경시키지 않고 복사한 객체의 프로퍼티 값만 변경할 수 있다. 이때, 새로운 객체를 선언하여 이 새로운 객체의 주소값에 기존 객체의 프로퍼티 값을 할당하면 객체 타입에도 깊은 복사를 적용할 수 있다. 다시 말해, 독립적인 메모리를 가진 새로운 객체를 복제할 수 있다.

따라서, 객체와 배열 타입의 state를 갱신할 때에는 이와 같이 spread 연산자를 활용하여 새로운 객체를 복제하고 setter 함수를 통해 그 객체에 새로운 값을 할당하면 된다. 이렇게 하면, REACT 컴포넌트의 상태 변경이 감지되어 리랜더링(컴포넌트 재실행)이 가능해진다.

원시 타입의 state 갱신

참고로, 원시 타입의 state는 setter 함수를 통해 값을 변경하기만 해도 서로 다른 값임이 인식되어 컴포넌트 재실행이 가능하다.

const [value, setValue] = useState(1);
setValue(2); // state 갱신
profile
There's Only One Thing To Do: Learn All We Can

0개의 댓글