
리액트 기초 11번째 강의는 불변성 & 순수함수에 대한 강의였다. 그런데 뭔가 내가 생각하던 개념과 반대(?)로 설명을 들어서 굉장히 혼란이 왔다. 객체나 배열이 불변성을 가지고 있다고 생각했는데 오히려 원시타입들이 그렇다고 하여 그냥 넘어갈 수 가 없었다. 그래서 다시 불변성에 대해서 공부하고 정리해보았다.

자바스크립트 엔진은 call stack과 heap memory 2가지 메모리 공간.
콜스택: 실행 중인 함수를 추적해 계산을 수행하고 지역변수를 저장하는 공간이다. 이곳에 원시 타입들이 저장된다.
힙 메모리: 참조 타입들이 할당되는 곳이다. 메모리 누수를 방지하기 위해 js 엔진의 메모리 관리자가 항상 관리하는 공간이다.
( 원시 타입 : Boolean, String, Number, null, undefined, Symbol
참조 타입 : Object, Array)
타입별로 데이터 저장방식과 할당 방식이 다름.

원시타입: 변수 a에 값 10을 저장을 했을 경우 콜 스택의 변수값에 10이 그대로 저장
참조타입: 변수 b,c,d에 array, object를 저장할 경우 실제 값은 메모리 힙에 저장되고, 메모리의 힙의 주소가 콜 스택의 값에 저장

원시타입 변수는 변수값을 변경하라는 명령을 받을 경우, 기존 콜스택의 값을 변경하지 않고 새로운 주소를 추가해 값을 저장하고 변수 b가 바라보게 한다.
이것이 불변성(메모리 영역의 값은 변경되지 않는다!) (더이상 참조되지 않는 데이터는 가비지 컬렉터가 메모리가 해제.)

a.에서 변수값 할당 후, b.에서 push를 통해 값을 추가하면?
실제로는 변수 a,b가 바라보고 있는 콜스택의 값이 변경되지 않고, 메모리 힙에 있는 데이터가 변경이 되어 불변성이 유지가 되지 않는다.
불변성을 지킨다의 의미는 메모리 영역에서 값을 변경할 수 없게 한다, 라는 의미.
왜냐하면, 리액트의 state 변화 감지 기준은 콜 스택의 주소값.
리액트는 콜 스택의 주소값만을 비교해 상태 변화를 감지, 이를 얕은 비교. 이것이 리액트의 state를 빠르게 감지할 수 있는 장점이자 불변성을 유지해야 하는 이유. 이것 때문에 불변성 중요.
-> 원시타입의 경우 값을 재할당할 경우 새로운 메모리가 할당되어 콜 스택의 주소 값이 감지가 된다.
-> 하지만 참조타입의 경우 참조타입의 값만 변경하면 실제로 콜스택의 주소값은 변경이 없어 state 감지가 되지 않아 리렌더링이 되지가 않음. 그래서 spread 연산자를 쓰는 등 새로운 배열과 오브젝트를 만들어 반환하는 이유.
배열을 setState 할 때 불변성을 지켜주기 위해, 직접 수정을 가하지 않고 전개 연산자를 사용해서 기존의 값을 복사하고, 그 이후에 값을 수정하는 식으로 만든다.
import React, { useState } from "react";
function App() {
const [dogs, setDogs] = useState(["말티즈"]);
function onClickHandler() {
// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다.
// 그리고 나서 항목을 추가합니다.
setDogs([...dogs, "시고르자브르종"]);
}
console.log(dogs);
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
하나 이상의 인자를 받고, 인자를 변경하지 않고, 참조하여 새로운 값을 반환하는 함수 즉, 같은 input동일한 인자가 전달되면 항상 동일한 결과를 반환하는 함수(코드 블록).
// 매개변수를 복사한 값을 변경하는 순수함수
const addSixPure = (arr) => {
// 펼침 연산자로 새로운 배열에 6 추가
newArr = [...arr, 6];
return newArr;
};
// 매개변수의 값을 직접 변경하는 불순함수
const addSixImpure = (arr) => {
// 매개변수에 직접 6 추가
arr.push(6);
return arr;
};
[출처 : https://velog.io/@fe_jungseok/리액트와-순수함수]