리엑트에서 중요하게 사용될 불변성에 관하여

Seong Hyeon Kim·2023년 12월 1일
0

프론트엔드

목록 보기
7/11
post-thumbnail

불변성

1) 불변성이란?

`불변성이란 메모리에 있는 값을 변경할 수 없는 것`을 말합니다. 

그래서 자바스크립트의 데이터 형태중에 원시 데이터 (숫자, 문자, 불리언) 들에게는 불변성이 있고, 원시 데이터가 아닌 객체, 배열, 함수는 불변성이 없습니다.


(2) 변수를 저장하면, 메모리에 어떻게 저장이 될까?

만약 우리가 let number = 1 이라고 선언을 하면, 메모리에는 1 이라는 값이 저장됩니다.


그리고 `number` 라는 **변수는 메모리에 있는 1을 참조**하죠. 그리고 이어서 우리가 `let secondNumber = 1` 이라고 다른 변수를 선언을 했다고 가정해봅시다.

이때도 자바스크립트는 이미 메모리에 생성되어 있는 1이라는 값을 참조합니다.

즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라보고 있는 것이죠. 그래서 우리가 콘솔에 number === secondNumber 를 하면 true가 보입니다.


불변성 확인을 위한 코드

import React from 'react'

function inde() {

    let a = 1
    let b = 1
    console.log("1번째 질문 : a 와 b 의 값은 같을까요?",a === b)
   // true

    let obj1 = {name:"kim"}
    let obj2 = {name:"kim"}


    console.log("2번째 질문 : obj1 과 obj2 의 값은 같을까요?",obj1 == obj2)
  //false
  
    console.log(`3번째 질문 : ${obj1.name}${obj2.name}의 값은 같을까요?`,obj1.name == obj2.name)
  // true  
  
  
  return (
    <div>
        불변성 체크 
    </div>
  )
}

export default inde

예시로 든 코드의 결과물들을 보면 원시데이터인 let 변수에 할당한 값들은

true로 나오고 있지만, 객체의 값은 실제 값을 비교하지 않는이상, false가

나오는것을 확인 할 수 있습니다.

이는 자바스크립트에서 원시데이터가 아닌 객체나 배열 등은 참조하는 주소가 다르기때문에

실제로 값이 같을지라도 비교하는 과정에서는 참조하는 주소가 다르기 때문에

비교연산자로 확인했을때에는 다르다고 나오는 이유입니다.



리액트에서는 컴포넌트가 화면에 그려지기 위해선 렌더링을 해야하고

그 렌더링이 되기 위한 조건은 스테이트가 변경되는것을 말한다.

그래서 단순 변수는 무시한다

function App(){

  const [inputTest, setInputTest] = useState("")  
  return(
    <div>
      <input 
      value={inputTest} 
      onChange={(event) => 
      setInputTest(event.target.value)} />

      <br />
        내가 입력한 값 = {inputTest}

    </div>
  )
}

예제 코드를 실행하고 나온 화면에서 볼수 있듯이 setState 를 사용해서 실시간으로 변경이되기 때문에 새로고침같은 전체 페이지를 처음부터 다시 불러오는 큰 단위 렌더링이 아닌 효율적인 렌더링이 될 수 있다


하지만 여기서 주의해야될 부분이 나옵니다.


리액트에서는 화면을 리레더링 할지 말지 결정할 때 state의 변화를 확인합니다. state가 변했으면 리렌더링 하는 것이고, state가 변하지 않았으면 리렌더링을 하지 않죠.


그때, state가 변했는지 변하지 않았는지 확인하는 방법이 state의 변화 전, 후의 메모리 주소를 비교합니다. 그래서 만약

리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 되는것이죠.

그래서 즉, 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 됩니다. 그래서 결국 마땅히 일어나야 할 리렌더링이 일어나지 않게되죠.



function App(){
  let buttonClickCount = 0;

  const [objTest, setObjTest] = useState({
    name : "에스파",
    members : 4
  })


  const onChangeHandler = () => {
    objTest.name = "레드벨벳"
    console.log(objTest)

    setObjTest(objTest)
  }




  return(
    <div>
      <div>
      그룹이름은 {objTest.name} 입니다.
      </div>

      <button
      onClick={onChangeHandler}

      >
      이름변경버튼
      </button>
    </div>
  )
}

export default App;

이것은 예시코드인데요, 이런식으로 작성했을때에 실제로 콘솔값을 확인해보면
실제로 objTest 의 내용은 변경이 되어있지만 리엑트는 스테이트 값이 변경되지
않았다고 인식되어 아무런 반응이 없는것을 확인할 수 있습니다.


그래서 코드를 변경해서 2가지 방법으로 이러한 문제를 해결할 수 있습니다.

1.setobj 로 바로 바꾸기

function App(){
  let buttonClickCount = 0;

  const [objTest, setObjTest] = useState({
    name : "에스파",
    members : 4
  })


  const onChangeHandler = () => {
    setObjTest({
      name : "레드벨벳",
    })
    console.log(buttonClickCount)
  }


  return(
    <div>
      <div>
      그룹이름은 {objTest.name} 입니다.
      </div>

      <button
      onClick={onChangeHandler}

      >
      이름변경버튼
      </button>
    </div>
  )
}

export default App;


2. 스프레드 연산자로 복사해서 사용하기.

function App(){
  let buttonClickCount = 0;

  const [objTest, setObjTest] = useState({
    name : "에스파",
    members : 4
  })


  const onChangeHandler = () => {
    const objTestCopy = {...objTest}
    objTestCopy.name = "레드벨벳"
    setObjTest(objTestCopy)
  }


  return(
    <div>
      <div>
      그룹이름은 {objTest.name} 입니다.
      </div>

      <button
      onClick={onChangeHandler}
    

      >
      이름변경버튼
      </button>
    </div>
  )
}

export default App;


리액트 불변성 지키기 예시

배열을 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;
profile
삽질도 100번 하면 요령이 생긴다. 부족한 건 경험으로 채우는 개발자

0개의 댓글