react

React 데이터 관리

리액트에서 가장 기본이라고 할 수 있는 부분은 상태 관리입니다. 상태 관리 라이브러리를 확인하기 보다는 이 글에서는 본질적인 데이터 관리에 대한 고찰을 해보려 합니다.


보통 우리가 말하는 crud란, 생성/읽기/변경/삭제를 나타냅니다. 코드를 작성하는 방법에 따라 다양한 방법으로 사용이 가능합니다. 여러 코드들의 확인하보고 관련 성능에 대한 비교도 같이 기술해 보려합니다. 일반적인 javascript에서 todo list를 만들면서 crud를 많이 구현해 보게 됩니다. 일반적인 Javascript와 react CRUD의 차이점은 Immutability(불변성)에 있습니다. 리액트는 상태의 변경을 감지하여 렌더링 트리를 새롭게 생성합니다. 이때 immutable하게 데이터를 변경하지 않는다면 메모리 주소의 데이터만 변경되기 때문에 React는 상태 변경을 감지 할 수 없습니다. 이러한 이유로 리액트는 Immutable한 상태값을 사용하여 Side Effect가 발생하지 않도록 해야합니다.

mutable한 상태 변경

첫 번째는 데이터 값을 직접 변경(mutate)하는 것입니다. 다른 말로 표현하면, Mutation한 데이터 수정라고 할 수 있습니다.

Mutable
let player = { credit: 1, name: 'py' }
player.credit = 2
 
// 결과값
// {credit: 2, name: 'py'}

Immutable한 상태 변경

두 번째는 원하는 변과 값이 포함된 새로운 복사본 데이터로 교체하는 것입니다. 다른 말로 표현하면, Mutation하지 않은 데이터 수정라고 할 수 있습니다.

Immutable
let player = { credit: 1, name: 'py' }
 
let newPlayer = Object.assign({}, player, { credit: 2 })
  • player는 변하지 않고, newPlayer만 {credit: 2, name: 'py'}로 변경되었습니다.
  • 위의 코드를 변경하여 spread 구문으로 사용하면, let newPlayer = {...player, credit: 2}로 작성해도 동일합니다. 주로 Spread 구문을 많이 사용합니다.

React에서의 불변성(Immutability)

Immutability 한 방식의 이점은 아래와 같습니다.

1. 복잡한 기능들이 단순해 진다. (Complex Features Become Simple)

공식문서에서는 Complex Features Become Simple 이런 표현을 썼습니다. 이 표현의 의미는 기존 데이터를 완전히 변화를 시키면, 이전 데이터로 복구하는 것이 불가능하다는 것을 의미합니다. 즉, React에서는 특정 작업을 취소 시 이전 작업으로 돌아가는 것이 가능하여, 재사용이 가능하다고 할 수 있습니다.

2. 변화를 감지할 수 있다. (Detecting Changes)

Mutable objects에서는 직접적으로 수정이 되기 때문에 변화를 감지하는 것이 어렵습니다. 그러나 Immutable objects에서는 단순히 참조하고 있는 객체가 이전 객체와 다르다면 변화한 것으로 판단이 가능합니다.

3. React가 다시 랜더할 시기를 결정할 수 있다. (Determining When to Re-Render in React)

React에서 immutability의 가장 큰 이점은 pure components를 만드는 것에 도움을 주는 것 입니다. 이 말은 리액트가 변화의 발생을 인지하고 component를 re-rendering을 가능하게 합니다. 좀 더 고민해본다면, 변화에 따른 re-rendering을 최적화 하기 위해 shouldComponentUpdate 나 React.PureComponent 를 사용 할 수 있는데, 이 부분은 성능최적화(Optimizing Performance)부분에서 다시 이야기를 해보도록 하겠습니다.


실습

데이터 만들기

대부분 Array내부에 객체가 포함되어 있는 형태로 JSON 데이터를 받습니다. 이러한 Array에 포함된 데이터의 단점은 CRUD 시에, 특정한 타겟 값을 찾을 때 리스트 내부의 객체들을 하나씩 들어가서 id값(unique)을 확인해야 합니다. 따라서 아래와 같이 변경하여 사용하는 것이 경우에 따라 좋은 성능을 만들 수 있습니다.

react
const [datas, setDatas] = useState({
  1: {
    id: 1,
    name: kang,
  },
  2: {
    id: 2,
    name: py,
  },
  3: {
    id: 3,
    name: han,
  },
})

위 코드는 아래의 Array의 id값을 Object key로 빼내어 변경한 코드입니다.

react
[
  {
    id: 1,
    name: kang,
  },
  {
    id: 2,
    name: py,
  },
  {
    id: 3,
    name: han,
  },
]

데이터 추가/수정

mutable
const createOrUpdateCard = (data) => {
  setDatas((datas) => {
    const updated = { ...datas }
    updated[data.id] = data
    return updated
  })
}
  • setDatas 내부에서 화살표 함수를 통해 이전데이터를 가지고 오는 로직입니다. 이러한 방식으로 가져오지 않고 그냥 datas를 쓰면, 데이터가 안들어가는 경우가 발생하게 됩니다.
  • 위의 로직으로 데이터 추가와 삭제를 동시에 관리할 수 있게 됩니다.

데이터 삭제

mutable
const deleteData = (data) => {
  setDatas((datas) => {
    const updated = { ...Datas }
    delete updated[data.id]
    return updated
  })
}
  • data로 인자를 받은 내용이 포함된 id값을 삭제하는 로직입니다. 이때 다른 일반적인 delete 로직과의 차이점은 map을 사용하지 않았다는 것입니다.

정리

데이터를 변경했는데, UI값이 변경되지않는 경우가 있습니다. 이러한 경우에 React를 Immutation하게 사용하였는지에 대한 고민이 필요해 보입니다.


Word

  • Mutation(변경)

    값을 직접 변경(ex. const로 선언된 array/object 내부 값 변경)

  • Reassign(재할당)

    새로운 데이터를 할당하여 데이터를 유지(ex. let 선언 변수의 string 값 변경)

  • Immutable : 원시 타입 (String, Number ...)

    Value 자체를 전달하므로 기존 데이터와 독립되어 있습니다.

  • Mutable : 참조 타입 (Object, Array ...)

    reference인 메모리 주소값을 전달하기 때문에, 변경 시 사용되는 다른 곳에서 영향을 주어 Side Effect가 발생갈 수 있습니다.


reference