React의 불변성이란?

해진·2024년 6월 25일

조각 테크 공유

목록 보기
10/16
post-thumbnail

들어가는 말)

react 라는 주제로 면접 스터디를 진행하였습니다. 이때 리엑트의 불변성과 관련된 질문이 많이 언급되었습니다.

이번 기회에 불변성이 어떤 성질이며 React에서 왜 불변성을 유지해야하는지 살펴보려 합니다.

[ 불변성이란? ]

불변성이란 말 그대로 변하지 않는 성질이라는 뜻을 가지고 있습니다.

그러나 이것이 왜 react에서 중요하지? 라는 생각이 가장 먼저 들었습니다.

이는 JS의 원시타입을 통해 조금 더 설명해보겠습니다.

  • 원시타입: Boolean, String, Number, Null, Undefined, Symbol
    • 고정된 크기로 메모리에 저장 된다. 실제 데이터가 변수에 할당(콜스택에 저장)
  • 참조타입: Object, Array
    • 데이터의 크기가 정해지지 않고 메모리에 저장, 데이터의 값이 heap에 저장되며, 변수에 heap 메모리 주소값이 할당

원시타입은 불변성을 가지고 있습니다. 그럼 값을 변경할 수 없다는 뜻으로 이해하기 쉬운데요


예시로
let string_sample = ‘test1’

string_sample = ‘test2’

위에서는 string_sample가 test1에서 test2로 변경되었습니다. 그러나 이는 그렇게 보이는 것입니다.

이는 string_sample변수가 test1에서 test2로 변경된 것 처럼 보이지만 실제 메모리 영역에는 test1, test2가 모두 존재합니다.

즉 string_sample: ‘test'이 메모리 1영역에 등록이 되고

string_sample: ‘test2’가 메모리 2영역에 새로 할당하게 됩니다.

즉 메모리 영역이 대체되는 것이아닌 새로 할당이 되는 것입니다. 그리고 이것글 불변성이라고 합니다.

다음은 참조 타입에 대한 불변성을 살펴보겠습니다.

let array_sample = [0,1,2,3,4] //메모리 영역 1
array_sample.push(5) //메모리 영역 1

array_sample = [0,1,2,3,4] //메모리 영역 2

위 array_sample에 push(5)를 함으로 원본 배열을 수정하는 즉 불변성을 지키지 않고 있습니다.

그리고 array_sample = [0,1,2,3,4] 위 문장은 원본 배열을 수정하는 것이 아니라 새 참조 값을 가진 새로운 배열을 할당하여 불변성을 지킵니다.

즉 불변성이란 메모리 영역에 할당된 값이 변하지 않는다고 말할 수 있습니다.

[ 왜 React에서 불변성을 지켜야 할까요? ]

위 파트에서 불변성이란 무엇인지를 살펴보았습니다. 그렇다면 리엑트에서는 이런 불변성을 어떤 이유에 지켜야하는 것일까요?

이는 리엑트의 상태 업데이트의 원리에 답이 있습니다.

리액트의 상태 업데이트

리엑트는 상태값을 업데이트 할 때 얕은 비교를 수행합니다. 이는 객체의 속성을 하나하나 비교하는 것이 아닌 이전 참조값과 현재 참조값만을 비교하여 상태 변화를 감지하게 됩니다.

이런 이유로 배열이나 객체를 업데이트 할 때 setState([…state, newState]) 등을 사용하여 새로운 참조값을 가진 배열이나 객체를 생성하는 방식을 사용합니다.

state의 불변성을 지키지 않는 즉 setState를 사용하여 state 값을 할당하는 것이 아닌 직접적으로 state값을 변경하게 된다면 이는 불변성을 지키지 않게 됩니다.

이렇게 직접적인 state의 수정은 새로운 참조값을 가지지 않기에 리액트가 state 값이 변경되었다는 것을 알 수 없어 상태변화를 감지하고 반영할 수 없습니다.

사이드 이펙트 방지

상태 변화와 함께 불변성을 지키게 된다면 사이트 이펙트를 방지할 수 있습니다.

외부에 존재하는 원본데이터를 직접 수정하지 않고, 원본 데이터를 복사하여 사용하기에 예상하지 못한 오류를 사전에 방지할 수 있습니다. 이는 다시 생각해보면 외부의 값을 함부로 변경할 수 있다는 것은 꽤나 위험한 일일 것입니다. 만약 다른 어딘가에서 원본 데이터를 사용하고 있는 파트가 있는데 이 값을 변경한다면 이는 사이드 이펙트가 일어날 가능성이 농후하기 때문입니다.

결론적으로 리액트는 불변성을 지킴으로 효과적인 상태 업데이트와 사이드 이펙트를 방지하는 이점을 가질 수 있습니다.

[ 어떻게 불변성을 지킬 수 있죠? ]


  • setState활용 앞서 많이 언급하였던 setState 함수를 활용하면 인수로 새로운 값을 할당하기에 이전 객체와는 다른 참조 주소를 가지게 됩니다. 이런 이유로 리액트는 이전 state와 새로운 state를 다른 주소를 참조하기에 변화된 값을 올바르게 변경할 수 있습니다.

  • 스프레드 연산자 사용 (…) 스프레드 연산자를 사용하면 해당 배열의 값을 꺼내어 새로운 배열이 값으로 할당 가능합니다. 따라서 새로운 배열에 이전 state값을 꺼내고 추가로 할당하거나 삭제 및 수정하면 불변성을 유지하며 값의 변화를 감지할 수 있습니다. 즉 state가 불변성을 유지하기 위해서는 항상 새로운 객체를 생성하고 이전 state 값을 복사한 후 변경된 값을 setState의 인수로 할당하여야 합니다.


  • Immer 라이브러리 사용 스프레드 연산자의 보다 쉬운 사용을 위해 나온 라이브러리




참고 글: https://hsp0418.tistory.com/171

profile
안녕하세요, Frontend 개발자 윤해진입니다.

0개의 댓글