본문 바로가기

Coding/TIL (Today I Learned)

state 불변성 관리, immer 라이브러리 써보기.

프로젝트를 진행하면서 전에 써보지 않은 redux-saga, hooks, styled-components 등을 적용해보고 있다. 또 한 가지는 immer 인데 코드 반영 전후로 필요성과 문법을 살펴보고 정리해둔다.

레퍼런스 : 

https://immerjs.github.io/immer/docs/example-reducer

https://react.vlpt.us/basic/23-immer.html


immer 가 무엇이고 왜 쓸까?

리액트에서는 배열이나 객체를 업데이트 할 때 직접 수정하지 않고 불변성을 지켜주면서 업데이트를 해야 한다. 
객체의 경우 Spread syntax (...)를 사용하고,  배열의 경우 push, splice 같은 함수 대신 concat, filter, map 등의 함수를 사용해야 함. 리덕스에서도 이와 같은 방식으로 state는 읽기 전용으로 관리되어야 한다. 

const object = {
  a: 1,
  b: 2
};

object.b = 3; // 이렇게 직접 변경하면 안됨!

const nextObject = {
  ...object, // 이런 방법으로 변경된 부분만 업데이트 한다!
  b: 3
};

데이터의 구조가 심플할 경우는 별 문제가 없지만 데이터 구조가 복잡하거나 뎁스가 깊다면 이런 불변성(기존 코드를 변경하지 않음)을 지키면서 새로운 데이터를 생성하는 코드를 작성하기 어려워진다. 많은 경우 코드가 어려워진다기보다는 단순한 내용인데 데이터의 구조 때문에 코드의 구조도 복잡해져서 가독성이 나빠지는 것. 이런 때 immer 라는 라이브러리는 데이터 불변성을 지켜줌. 를 사용하면 좀 더 깔끔한 코드를 작성할 수 있다.

 

immer는 리듀서에서나, 컴포넌트에서 setState 로 스테이트 관리를 해 줄 때 활용해볼 수 있다.

나는 이번에 리듀서에서 immer를 우선 적용해보리고 했다. 이번 프로젝트의 스테이트 관리는 매우 심플한 수준이지만, 사용해 보는데 의의를 두었다. 하지만 성능적으로는 immer를 사용하지 않은 코드가 좀 더 빠르다고 하니, 실제로 활용 여부는 상황에 따라 고려가 필요할 것 같다. 심플한 데이터 구조라면 사용하지 않는 편이 코드가 더 깔끔하기도 하고.  

 

참고한 벨로퍼트님의 의견

  • 가능하면 데이터구조가 복잡해지지 않도록 할 것.
  • 어쩔 수 없을 때는 필요한 곳에만 immer 를 쓸 것.
  • 간단히 처리될 수 있는 곳에서는 일반 JavaScript로 구현할 것. 

 

Immer  적용 코드

라이브러리 인스톨 후 immer는 보통 produce 라는 이름으로 import 한다. 

import produce from 'immer'

리듀서 부분에 적용해봤는데 현재는 적용한 게 더 복잡해 보인다. ㅎㅎ 

import produce from 'immer';

const initialState = {
  videos: [],
  selectedVideo: []
};

// action type
export const LOAD_VIDEO = 'LOAD_VIDEO';
export const SELECT_VIDEO = 'SELECT_VIDEO';
export const LOAD_VIDEO_SUCCESS = 'LOAD_VIDEO_SUCCESS';

// action creators
export const loadVideo = searchTerm => ({
  type: LOAD_VIDEO,
  searchTerm
});

export const loadVideoSuccess = videos => ({
  type: LOAD_VIDEO_SUCCESS,
  videos
});

export const selectVideo = selected => ({
  type: SELECT_VIDEO,
  selected
});

//Reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case LOAD_VIDEO: {
      return {
        ...state
      };
    }
     case LOAD_VIDEO_SUCCESS: {
      console.log('action.data :', action.data);
      return produce(state, draft => {
        draft.videos.push(action.data);
      });
    }
    case SELECT_VIDEO: {
      console.log('action.selected :', action.selected);
      return produce(state, draft => {
        draft.selectedVideo.push(action.selected);
      });
    }
    default: {
      return state;
    }
  }
};

export default reducer;

 

 

공부할 라이브러리가 정말 많기도 하다.