리액트 - Immer를 사용하여 쉬운 불변성 지키기

정영찬·2022년 2월 24일
0

리액트

목록 보기
25/79

리액트에서 배열이나 객체를 업데이트해야 할 때에는 직접 수정을 하면 안되고 불변성을 지켜주면서 업데이트를 해줘야한다고 몇번 언급했었다.

데이터의 구조가 복잡하고 까다로워진다고 해도 불변성은 지켜가면서 새로운 데이터를 생성해야하는데, 여기서 immer라는 라이브러리를 사용하면 더 단순하게 구현이 가능하다.

제일먼저 해야할 일은 immer 라이브러리를 설치하는 것이다.

yarn add immer

그다음 immer를 사용하고 싶은 파일에 가서

import produce from 'immer';

를 작성한다.

연습을 해보자. 검사로 들어가서 콘솔창을 띄우고 배열을 하나 만든뒤, 그 배열의 내용을 건드리지 않고, 새로운 내용을 추가한 새로운 배열을 만든다.

먼저 배열 생성

const array =[
    {id: 1, text: 'hello'},
    {id: 2, text: 'bye'},
    {id: 3, text: 'whatever'}
    ]

새로운 배열을 선언한다.

const nextArray = produce(array, draft => {
    draft.push({id: 4, text: 'hi there' });
    draft[0].text = draft[0].text +'world';
});

draft는 지금 array와 똑같은 것이라고 보면 되고, 이 draft에 새로운 항목(hi there)를 추가했으며 첫번째 항목의 내용에 'world'를 추가한다는 내용이다.

이렇게 한뒤 nextArray를 콘솔창에 불러오면

0: {id: 1, text: 'helloworld'}
1: {id: 2, text: 'bye'}
2: {id: 3, text: 'whatever'}
3: {id: 4, text: 'hi there'}
length: 4
[[Prototype]]: Array(0)

그럼 array는 어떨까?

0: {id: 1, text: 'hello'}
1: {id: 2, text: 'bye'}
2: {id: 3, text: 'whatever'}
length: 3
[[Prototype]]: Array(0)

항목의 내용들이 모두 그대로 있다.

이렇게 immer 라이브러리의 draft를 사용하면 쉽게 불변성을 유지하면서 배열의 업데이트가 가능해진다.

그럼 이제 리듀서에서 immer 를 사용해보자
기존에 만들었던 App.js에서 실습한다.

먼저 리듀서의 'CREATE_USER' action.type를 수정한다.

 case 'CREATE_USER':
      return produce(state, draft => {
        draft.users.push(action.user);
      })

state의 users 배열에서 action.user값을 추가한다는 뜻이다.

이전에 작성한 내용과 비교해보면

return{
        inputs: initialState.inputs,
        users: state.users.concat(action.user)
      }

그렇게 엄청 단순해지지는 않았다. immer 라이브러리를 사용한다고 해서 무조건 단순하고 편해지는 것은 아니라는 사실을 기억하자.

CREATE_USER의 경우는 실행되는 내용이 단순해서 그렇지만 만약 조금더 복잡하다면 immer라이브러리의 효과는 상승한다.

그럼 이제 'TOGGLE_USER'를 immer 라이브러리를 사용해서 수정해보자.

case 'TOGGLE_USER':
      return produce(state, draft => {
        const user = draft.users.find(user => user.id === action.id);
        user.active = !user.active;
      })

더 직관적으로 바뀌어서 어떤 작업을 수행하는지 쉽게 알수 있게 되었다.

이전의 내용은 이렇다.

return{
        ...state,
        users: state.users.map(user=>
          user.id === action.id
          ? {...user, active: !user.active}
          :user )
      }

immer 라이브러리는 필수로 사용해야하는 것은 아니다. 단지 업데이트 할때의 작업 내용이 복잡하다고 느껴진다면 immmer 라이브러리를 사용해서 간단하게 만들어줄 수 있다는 것이다.

※ produce에서 파라미터를 draft로만 작성하고 함수의 내용을 넣는다면, updater가 반환된다

const [todo, setTodo] = userState({
	text: 'Hello',
    done: false
});

const onClick = useCallback(() => {
	setTodo(
    	produce(draft => {
        draft.done = !draft.done;
        })
       );
      },[]);
profile
개발자 꿈나무

0개의 댓글