[React] 불변성과 순수함수
1. React의 생명주기
React의 컴포넌트는 생명주기라는 것이 있다.
React의 컴포넌트는 생성(mounting), 업데이트(updating), 제거(unmounting)의 생명 주기를 갖게 된다.
이때, 컴포넌트가 생성되고 업데이트 되는 과정을 render라고 한다.
2. 불변성 개요
2.1 불변성의 개념
불변성에 대해 자세히 알고 싶다면 《 Date Type의 메모리 할당 》포스팅을 보고오도록 하자.
원시형(불변성o)
변수를 저장하면, 메모리에 어떻게 저장이 될까?
// 원시형(숫자, 문자, 불리언, null...)
let number = 1;
let secondNumber = 1;
위 코드와 같을 때 let number = 1
이라고 선언을 하면, 메모리에는 1 이라는 값이 저장되고 number
라는 변수는 메모리에 있는 1을 참조한다.
이어서 let secondNumber = 1
은 이미 메모리에 생성되어 있는 1이라는 값을 참조한다.
즉, number와 secondNumber는 변수명은 다르지만, 같은 메모리의 값을 바라보고 있는 것이다.
console.log(number == secondNumber); // true
기존에 1이던 number를 number = 2
라고 새로운 값을 할당하면 메모리에서는 어떻게 될까?
let number = 1;
let secondNumber = 1;
number = 2;
console.log(number == secondNumber); // false
원시형은 불변성이 있기 때문에 기존 메모리에 저장이 되어 있는 1이라는 값이 변하지 않고, 새로운 메모리 저장공간에 2가 생기고 number라는 값을 새로운 메모리 공간에 저장된 2를 참조하게 된다.
즉, 메모리 주소가 변한 것이다. → 불변성이 있다.
참조형(불변성x)
// 참조형(객체, 함수, 배열)
let obj_1 = { name: "kim" };
let obj_2 = { name: "kim" };
console.log(obj_1 === obj_2); // false
참조형의 경우 obj_1과 obj_2는 다른 주소값을 갖게 되어 false
가 출력되는 것을 알 수 있다.
obj_1.name = ‘park’
이라고 새로운 값을 할당하면 어떻게 될까?
let obj_1 = { name: "kim" };
let obj_2 = { name: "kim" };
obj_1.name = "park";
console.log(obj_1 === obj_2); // false
참조형은 불변성이 없기 때문에 기존 메모리 저장공간에 있는 {name: "kim"}
이라는 값이 {name : "park"}
으로 바뀌어 버렸다.
즉, 메모리 주소가 변하지 않았다. → 불변성이 없다.
정리
원시형은 수정을 했을 때 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장공간에 새로운 값을 저장한다.
참조형은 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장공간의 값 자체를 바꿔버린다.
2.2 React의 불변성
왜 리액트에서는 참조형의 불변성을 지켜주는 것을 중요시할까?
리액트에서는 컴포넌트가 화면에 그려지기 위해 렌더링을 한다.
리액트는 화면을 렌더링할지를 state의 변화에 따라 결정한다. 단순 변수는 무시한다! ⭐⭐
state가 변했으면 화면에 렌더링하고, 변하지 않았으면 렌더링하지 않는 것이다.
아래의 예시를 보자
import React, { useState } from "react";
function App() {
const [input, setInput] = useState("");
return (
<div>
<input
value={input}
onChange={(event) => {
setInput(event.target.value);
}}
/>
{input}
</div>
);
}
export default App;
입력 필드에 사용자가 텍스트를 입력할 때마다 state가 변경되면서 실시간으로 해당 값이 화면에 반영이 된다.
이처럼, 화면이 바로바로 갱신되는게 렌더링이다.
state가 변했는지 변하지 않았는지 확인하기 위해 state의 변화 전, 후의 메모리 주소를 비교하게 된다.
리액트에서 참조형을 수정할 때 불변성을 지켜주지 않고 직접 수정을 가하면, 값은 바뀌지만 메모리주소는 변함이 없게 되는것이다.
아래의 예시를 보자
import React, { useState } from "react";
function App() {
let count = 0;
return (
<div>
<button
onClick={() => {
count++;
console.log(`count : ${count}`);
}}
>
증가 버튼
</button>
{count}
</div>
);
}
export default App;
console에는 count가 증가하고 있지만 화면에는 증가하지 않는다는 것을 알 수 있다.
즉, 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 되어 렌더링이 되지 않는다.
정리
원시형의 경우 무조건 불변성을 보장하지만, 참조형의 경우 값을 변경하더라도 주소가 변하지 않아 화면이 렌더링이 되지 않기 때문에 불변성을 지켜주는 것이 중요하다!⭐⭐
2.3 React 불변성 보장하기
참조형을 setState 할 때, 직접 수정을 가하지 않고 전개 연산자를 사용해서 기존의 값을 복사하고, 그 이후에 값을 수정하는 식 다른 주소값을 바라보게 하여 불변성을 보장할 수 있다.
spread operator 뿐만 아니라 map, filter을 사용하여 불변성을 보장할 수 있다.
-
예시(배열) ①
“spread operator”를 사용하여 기존 배열을 복사하고, 그 뒤에 항목을 추가하여 새로운 배열을 만들어 상태를 업데이트해보자.
import { useState } from "react"; function App() { const [dogs, setDogs] = useState(["말티즈"]); const handleAddBtn = () => { // spread operator(전개 연산자)를 이용해서 dogs를 복사한다. // 그리고 나서 항목을 추가한다. setDogs([...dogs, "푸들"]); }; console.log(dogs); return ( <div> <button onClick={handleAddBtn}>동물 추가</button> </div> ); } export default App;
-
예시(객체) ②
import { useState } from "react"; function App() { const [dogs, setDogs] = useState({ name: "코리", breed: "푸들" }); const handleAddBtn = () => { setDogs({ ...dogs, name: "초롱이" }); }; console.log(dogs); return ( <div> <button onClick={handleAddBtn}>이름 변경</button> </div> ); } export default App;
3. 순수함수 개요
3.1 순수함수의 개념
하나 이상의 인자를 받고, 인자를 변경하지 않고, 참조하여 새로운 값을 반환하는 함수를 말한다.
즉, 같은 input동일한 인자가 전달되면 항상 동일한 결과를 반환하는 함수(코드 블록)이다.
3.2 순수함수 예시
순수함수o
// 매개변수를 복사한 값을 변경하는 순수함수
const addSixPure = (arr) => {
// 펼침 연산자로 새로운 배열에 6 추가
newArr = [...arr, 6];
return newArr;
};
순수함수x
const num_arr = [1, 2, 3, 4, 5];
// 매개변수의 값을 직접 변경하는 불순함수
const addSixImpure = (arr) => {
// 매개변수에 직접 6 추가
arr.push(6);
return arr;
};
3.3 React와 순수함수
컴포넌트는 독립적이고 재사용 가능한 기능 단위로, 애플리케이션을 구성하는 작은 부분이다.
반면 순수함수는 입력값에만 의존하고 외부 상태를 변경하지 않는다.
순수함수는 부작용(side effect)이 없기 때문에 동일한 입력에 대해 항상 동일한 결과를 반환한다.
따라서 컴포넌트 내부에서 순수함수를 사용하면 예측 가능하고 테스트하기 쉬운 코드를 작성할 수 있다.
댓글남기기