[React 기초 다지기] React의 불변성

YUZAMIN
Hello, World! I'm YUZAMIN, a diligently endeavoring frontend developer 🐤💦
[React 기초 다지기] React의 불변성

React의 불변성

불변성이란(Immutability)

불변성

  • 불변성
    • 메모리 영역에서 상태나 값을 변경할 수 없는 것

JavaScript 원시 타입과 참조 타입

  • 자바스크립트의 원시 타입(String, Number, Bigint, Boolean, Undefined, Symbol, Null)
    • 메모리에 고정된 크기로 데이터가 저장되며 변수에 매칭된 주소의 메모리 공간에 해당 원시 데이터의 ‘값’을 저장함
    • 기존 선언 및 할당된 변수의 값을 변경할 시, 변수에 매칭된 메모리 공간의 값이 a에서 b로 바뀌는 것이 아닌, 다른 메모리 공간에 b가 저장된 후, 변수가 b의 메모리 주소와 매칭되는 방식
      • 메모리 공간 내에서 값을 변경 불가능 -> 완전히 새로운 메모리 공간에 새로운 값이 저장됨
      • 기존 값인 a는 메모리 청소기인 Garbage Collector에 의해 사라짐
    • 즉, 원시 타입의 값은 변경이 불가능하며, 이를 ‘불변성을 유지한다’고 표현
  • 자바스크립트의 참조 타입(Object, Array, Function)
    • 크기가 정해져있지 않고 변수는 해당 데이터 자체를 저장하는 것이 아니라, 변수에 매칭된 주소의 메모리 공간에 해당 데이터가 저장된 메모리 블록의 ‘주소’를 저장함
      • 동적으로 크기가 변하는 데이터를 보관하기 위해 데이터를 메모리에 저장한 뒤 변수에 매칭된 주소의 메모리 공간에는 데이터가 저장된 메모리 블록의 주소를 할당
    • 참조 타입의 데이터들은 변할 수 있는 값이며, 새로운 값이 만들어지지 않고 해당 데이터를 직접적으로 변경할 수 있음
    • 배열에 push() 메소드를 사용하여 값을 추가할 때, 해당 배열이 저장된 메모리 공간 내에 추가된 값이 할당된 주소가 추가되는 방식
      • 메모리 공간 내에서 값을 변경할 수 있음
    • 즉, 참조 타입의 값은 변경이 가능하며, 불변성을 유지하지 않을 수 있음
1
2
3
4
5
6
7
8
9
// 원시 타입
let num = 100 // 메모리에 Number 타입의 100이라는 값이 고정된 크기로 저장되며 num이 해당 원시 데이터의 값을 보관 = 변수에 할당된 메모리 블록에 100을 저장
num = 200 // 200이라는 새로운 값을 저장할 새로운 메모리 공간 확보 후 거기에 200이 저장되며 num은 200이 저장된 메모리 공간의 주소와 재매칭됨

let copy = num // 200이라는 값 자체를 복사하여 새로운 메모리 공간에 저장한 뒤, copy는 그 메모리 공간의 주소와 매칭됨
num = 300 // 300은 새로운 메모리 공간에 저장되며 num은 그 공간의 주소와 매칭됨
console.log(copy) // copy의 값은 그대로 200

// 참조 타입

불변성을 지켜야 하는 이유

  • 예를 들어, [1, 2, 3]이라는 배열이 할당된 state가 있다면, 이 배열에 4를 추가로 push하면 리액트는 해당 state의 변화를 감지하지 못함
    • 리액트는 prevState !== currentState여야 state가 바뀌었다고 판단하여 재랜더링함 -> 예전 state와 현재 state의 참조가 달라야함
    • 리액트는 객체의 속성 하나하나를 비교하지 않고 참조값만 비교함 -> push() 메소드 사용 시 state가 참조하고 있는 메모리의 주소는 변화하지 않으며, 객체 자체에 값이 추가되어 불변성 지켜지지 않음
    • 따라서 리액트는 객체의 속성이나 값을 바꾸는 것을 ‘state가 변화되었다’고 인지하지 못함
  • 리액트는 불변성을 지켜야 하므로 객체(배열, 함수 등)를 함부로 바꾸면 안됨
    • 객체를 함부로 바꾸지 말고 복사해야 함: 객체를 바꾸지 말고 기존 객체를 복사하여 새로운 객체를 만들어서 그것을 수정한 뒤, 수정된 객체로 기존 객체를 대체해야 함 = state가 참조하고 있는 주소값 자체를 바꿔야 함
    • 따라서 배열을 직접적으로 수정하는 pop, push, shift, unshift, splice 등의 메소드들이 아닌 새로운 배열을 만들어내는 concat, slice와 같은 메소드들을 사용해야 함
    • 리액트의 state를 변경할 때에도 state에 값을 직접 할당하는 방식이 아닌, setState()를 사용해야 함
1
2
this.state.number = 2 //(X)
this.setState({ number: 2 }) //(O)

참고1 참고2 참고3