[TIL #16] React - immer 란? (+ 불변성을 지켜야하는 이유)

JMinkyoung·2021년 8월 11일
2

TIL

목록 보기
16/42
post-thumbnail


Reducer를 작성하면서 불변성을 지키기 위해 immer라는 라이브러리를 사용하였다.
평소에 코딩을 하면서 불변성을 지켜야지~ 지켜야지~ 달달 외우고 살긴 했지만 '왜? 안지키면 어떻게 되는데?' 라는 의문을 단 한번도 가져보지 않았다..🙄
immer를 사용한 김에 immer가 정확히 어떤 역할을 하는지, 불변성을 지켜야 하는 이유에 대해서 알아보자 🏃‍♂️

immer 란?

React에서 불변성을 유지하느라 복잡해진 코드를 짧고 간결하게 작성할 수 있도록 도와주는 라이브러리를 의미한다.

불변성을 지키는 이유

불변성이란 기존의 상태 값을 유지하면서 새로운 상태 값을 추가하는 것 을 의미한다.

React 에서는 얕은 비교를 통해 새로운 값인지 아닌지를 판단 한 후, 만약 새로운 값임을 판단하게 되면 부모 컴포넌트가 리렌더링을 하면 자식 컴포넌트도 함께 리렌더링 되게 된다.

  • 만약 어떠한 컴포넌트를 리렌더링 하고 싶을 때 배열 타입인 state가 있다고 가정하자.
  • state.push(10)을 통해서 배열에 직접 10이라는 값을 추가한다.
  • React에서는 해당 state라는 값은 새로운 참조값으로 바뀐것이 아니기 때문에 push 이전의 state와 push 이후의 state가 같다고 판단하여 리렌더링을 하지 않게 된다.

🍀이러한 이유 때문에 state 값을 변경하고 React에게 리렌더링을 원한다고 알리고 싶다면 새로운 배열을 생성해서 새로운 참조값을 생성하고 그 안에 기존의 값을 넣어줘야 한다!🍀

얕은 비교란?
객체, 배열, 함수 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일한 참조인지만을 비교하는 것을 의미!

immer 예제

const baseState = [
    {
        title: "Learn TypeScript",
        done: true
    },
    {
        title: "Try Immer",
        done: false
    }
];

const nextState = produce(baseState, draft => {
    draft[1].done = true;
    draft.push({title: "Tweet about it"});
});

immer에서는 produce라는 함수를 사용하는데 첫번째 매개변수로는 수정하고 싶은 값(객체나 배열), 두번째 매개변수로는 첫번째 매개변수에 할당된 값을 바꾸는 함수이다.

실제로 Reducer에 적용한 코드도 한번 살펴보자!

immer 적용 전 코드

const initialState = {
    name: '푸푸',
    songs: { title: '느린여행' }
};
  
const reducer = (state = initialState, action) => {
	switch (action.type){
            case SET_USER:
              return {
                ...state,
                name:'김채원',
                songs: {
                  ...state.songs,
                  title:'내손을잡아'
                }
              };
            default:
              return state;
    }
};

...state를 통해 state를 펼쳐주고 ...state.songs를 통해 state안의 songs 또한 펼쳐주고 값을 수정하게 된다. 만약 state가 더 많은 정보를 가지고 있다면 이 코드는.. 더 길어지고 가독성도 떨어지게 될 것이다.

immer 적용 후 코드

const initialState = {
    name: '푸푸',
    songs: { title: '느린여행' }
};
  
const reducer = (state = initialState, action) => {
    return produce(state, (draft) => {
        switch (action.type){
          case SET_USER:
            draft[0].name = action.data.name;
            draft[0].songs.title = action.data.title;
            break;
          case ADD_USER:
            draft.push({name: '김채원', songs: {title:'내손을잡아'}});
            break;
          default:
            return draft;
        }
    });
};

draft[0].name = action.data.namedraft.push({name: '김채원', songs: {title:'내손을잡아'}})를 통해 기존의 state를 복사할 필요 없이 값을 수정하고 새로운 값을 추가할 수 있다! (immer의 경우에는 각 case마다 끝에 break를 꼭 붙여줘야한다!)

참고자료

profile
Frontend Developer

0개의 댓글