본문 바로가기
FE/React

[React Official Document] #6 Updating object on state

by aeyong-dev 2023. 10. 4.

리액트의 상태로는 다양한 타입이 할당될 수 있다. 

우리는 숫자, 문자열, 불리언 등의 값들을 사용했는데, 객체 또한 상태가 될 수 있다. 

하지만, 우리는 불변성을 유지하기 위해 객체를 바꿔서는 안된다. 

객체를 바꾸는 것 대신, 새로운 객체를 만들어서 기존 객체와 교체해야한다. 

내부의 값을 바꾸는게 아니라, 객체 자체를 교체한다는 뜻이다. 

이 문서에서는 객체인 상태를 업데이트, 중첩된 객체 업데이트, 불변성, Immer를 활용하여 간단하게 객체 상태를 업데이트 하는 방법을 배울 것이다. 


Mutation(변경)이란?

숫자, 문자열, 불리언, 객체 등 모든 자바스크립트 타입은 객체가 될 수 있다. 

여기서 숫자, 문자열, 불리언 등은 불변적인 값인데, 변할 수 없고 read-only인 것으로 받아들여도 된다. 

예를 들어, n에 대한 상태관리가 된다고 하자(숫자). 

n의 초기값은 0이고, 나중에 setN(5) 함수가 실행되어 5가 되었다. 

여기서 n은 0에서 5로 변한게 아니라 교체된 것이다. 

n이라는 바구니에 0이 적혀있는 공을 빼고 5가 적혀있는 공을 넣은 것이라 할 수도 있겠다. 

공의 숫자가 0이었다가 5로 변한게 아니라, 공 자체가 교체된것이다. 

n의 값(공에 적힌 숫자)을 바꾸는 것은 불가능하다. 

object.name='temp' 와 같은 식으로 바꿀 수는 있지만, 값이 변경되어버린다. 

가능은 하지만.. 불가능한 것 처럼 값을 다루어야 한다. 

왜? 왜 굳이 그래야할까?

 

상태를 읽기 전용인 것 처럼 다루기

상태인 객체는 읽기 전용인 것 처럼 다루어져야 한다. 

상태 객체에 쓰기(수정하기)는 불가능하다는 뜻이다. 

setState()함수 없이 값이 변하게 된다면 리액트는 상태 값의 변화를 감지하지 못한다!

그래서 우리가 불변성(Immutbility)을 유지해야 하는 것이다. 

이전 포스팅에서 식당의 웨이터를 예로 들었다. 

불변성을 지키지 않는 상황을 식당의 상황으로 비유한다면, 손님이 식사를 모두 마치고 주문을 수정하는 것과 같다. 

 

특별한 문법으로 객체 복사하기

새로운 객체에 이전 데이터를 포함하고싶다면 전개연산자를 사용할 수 있다. 

전개연산자는 얕은 복사이고, 중첩된 객체는 전개연산자를 여러 번 사용해야한다. 

아래는 전개연산자의 사용 예시이다. 

const obj1 = {
	name: 'obj1',
    age: 24, 
    gender: 'male'
}

const obj2 = {
	...obj1, 
    age: 25
}

 

중첩된 객체를 업데이트하기

아래와 같은 객체가 있다고 가정해보자. 

const person = {
	name: 'kim',
    artwork: {
    	title: 'truth',
        price: 100
    }
};

artwork의 title을 바꾸고싶다면 어떻게 해야할까? 

두 가지 방법이 있다. 

const newArtwork = {
	...person.artwork,
    title: 'love'
};

const newPerson = {
	...person, 
    artwork: newArtwork
};

setPerson(newPerson)
setPerson({
	...person,
    artwork: {
    	...artwork,
        title: 'love'
    }
});

당연히 아래의 방법이 간결하고 가독성이 좋다. 

하지만 객체의 중첩이 더 깊어진다면 복잡하기는 매나 같을 것이다. 

그래서 immer라는 것이 나왔다. 

 

Immer로 간결한 업데이트 로직 작성하기

immer는 불변성을 지키게 도와주는 라이브러리이다. 

사용법은 간단하다. 

npm install use-immer로 설치하고, import {useState}~ 대신 import {useImmer} from 'use-immer'를 사용해준다. 

아래의 예시를 참고하자. 

import {useImmer} from 'use-immer'

...

const [person, setPerson] = userImmer(
	{
    	name: 'kim',
        artwork: {
        	title: 'love',
            price: 100
        }
    });
    
...

updatePerson(draft => {
	draft.artwork.title = 'trust'
});

원래대로라면 불변성이 깨져야하지만, immer를 사용하면서 불변성을 유지할 수 있다. 

사용법도 useState 대신 useImmer를 쓰기만 하면 돼서 간편하다.