React

[React] 리액트에서 불변성(Immutability) 관리의 중요성과 방법

_doit 2024. 8. 21. 19:03
728x90
반응형

리액트(React)에서 상태 관리는 애플리케이션의 성능과 유지보수성에 큰 영향을 미친다. 상태를 관리할 때 불변성을 유지하는 것은 매우 중요하다. 특히 객체 타입(Object, Array)을 다룰 때는 불변성을 지키는 것이 더욱 중요하다. 그렇다면 객체 타입에서 불변성을 유지해야 하며, 어떻게 유지할 수 있을까?

1. 불변성이란?

불변성(Immutable)이란 변하지 않는 상태를 유지하는 것 . 리액트에서는 상태가 변경될 때마다 해당 컴포넌트를 다시 렌더링하는데, 이때 불변성을 유지하지 않으면 상태 변경을 감지하기 어려워지고, 의도하지 않은 버그가 발생할 수 있다.

2. 원시 타입 vs. 참조 타입

자바스크립트에서 불변성을 이해하기 위해, 기본 타입(원시 타입)과 참조 타입의 차이를 먼저 알아야 한다.

  • 원시 타입: 불변성을 가지고 있다. 값이 변경되면 새로운 메모리 주소가 할당됩니다.
    • 예: Boolean, String, Number, null, undefined, Symbol
    • 고정된 크기: 원시 타입의 값은 고정된 크기를 가지고 있으며, 메모리의 Call Stack에 저장된다.
    • 값의 변경: 값을 변경하면, 실제로는 기존 값을 변경하지 않고 새로운 메모리 위치에 새로운 값이 저장된다
    • 예를 들어, 문자열을 생각해봅시다:

 a,b 값이 바뀌면 기존 값을 수정하지 않고  새로운 메모리 위치를 참조하게 된다. 값 자체는 변하지 않고 새로운 값이 할당되는 것이다. 이게 바로 원시 타입의 불변성이다.

 

  • 참조 타입: 불변성이 없다. 값을 변경하면 같은 메모리 주소에서 변경이 일어난다.
    • 예: Object, Array
    • 참조 타입의 데이터는 크기가 고정되어 있지 않기 때문에, 메모리에서 값을 직접 저장하는 것이 아니라, Heap 메모리에 저장되고, 메모리 주소를 통해 값을 참조한다

위 코드에서 배열에 각각 100과200을 추가하면, array가 가리키는 메모리 위치는 그대로 유지되지만, 그 위치에 저장된 값이 변경된다. 즉, 배열의 원본 데이터가 수정된 것이고, 이는 불변성이 지켜지지 않은 것이다.

불변성 요약

  • 원시 타입: 값이 변경될 때 새로운 메모리 위치에 저장되므로 불변성이 자연스럽게 유지됩니다.
  • 참조 타입: 값이 변경될 때 같은 메모리 위치에서 변경이 이루어지므로, 불변성을 유지하려면 새로운 객체나 배열을 만들어야 한다.

따라서 참조 타입의 경우는 불변성에 대해 신경써줘야한다!!

3. 왜 리액트에서 불변성을 지켜야 할까?

리액트는 상태를 업데이트할 때 얕은 비교(shallow comparison)를 사용한다. 얕은 비교란, 객체의 모든 속성을 하나하나 비교하지 않고, 참조 값(메모리 주소)만 비교하여 상태 변화를 감지하는 방법이다.

불변성을 지키는 이유:

  1. 예상치 못한 오류 방지: 참조 타임에서 객체나 배열의 값이 변할 때 원본 데이터가 변경되기에 이 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있어서 프로그래밍의 복잡도가 올라간다
  2. 효율적인 상태 업데이트: 리액트에서는 상태 업데이트 시 기본적으로 참조 값을 비교하는 "얕은 비교"를 사용한다. 만약 원본 데이터를 직접 변경하면 참조 값은 동일하게 유지되기 때문에, 리액트는 이 변화를 감지하지 못할 수 있 다. 그러나 불변성을 유지하면 새로운 참조 값을 생성하므로, 리액트가 상태 변화를 정확하게 감지할 수 있고, 이로 인해 불필요한 렌더링을 방지하여 성능이 향상된다.

불변성을 지키는 방법

참조 타입에서는 값을 바꿨을 때 Call Stack 주소 값은 같으나 Heap 메모리 값만 바꿔주기 불변성을 유지할 수 없으므로 새로운 배열을 반환하는 메소드를 사용하는 것이 좋다.

spread operator (...):, map, filter, slice, reduce

↔  원본 데이터를 변경하는 메소드:  splice, push

 

1 상태 업데이트 시

const array = [1, 2, 3, 4];
const sameArray = array;
sameArray.push(5);
console.log(array === sameArray); // true

const array = [1, 2, 3, 4];
const differentArray = [...array, 5];
console.log(array !== differentArray); // false

 

2 라이브러리 사용

  • Redux Toolkit (RTK): RTK는 불변성을 유지하면서 상태를 업데이트하는 기능을 제공하여, 리덕스 상태 관리를 더욱 간단하게 만들어준다. RTK는 내부적으로 Immer.js를 사용하여 상태를 불변하게 유지한다.
  • Immer.js: Immer.js는 상태를 불변하게 처리하면서도 직관적인 코드 작성을 가능하게 해주는 라이브러리. Immer.js를 사용하면 복잡한 상태 업데이트 로직도 쉽게 작성할 수 있다.
import produce from 'immer';

const nextState = produce(currentState, draftState => {
  draftState.name = 'New Name';
});

 

결론

불변성은 리액트에서 효율적인 상태 관리와 리렌더링을 위한 핵심 개념이다. 자바스크립트의 원시 타입과 참조 타입에 대한 이해를 바탕으로 불변성을 유지하는 방법을 익히면, 리액트 애플리케이션의 안정성과 성능을 크게 향상시킬 수 있다. 데이터 변경을 추적하고, 불변성을 지키는 다양한 방법들을 활용하여, 예기치 못한 오류를 방지하고 코드의 예측 가능성을 높일 수 있다.

 

 

-끝-

728x90
반응형