본문 바로가기

FRONTEND

React 상태 불변성

출처: https://unsplash.com/ko/%EC%82%AC%EC%A7%84/oBTEoj6lbCE

 

최근 면접을 진행하면서 React state 불변성에 대한 질문에 명쾌하게 대답하지 못하여 개인 학습 차원의 목적으로 해당글을 작성합니다. 혹시 잘못된 부분 관련 피드백 주시면 즉각적으로 반영하도록 하겠습니다.

 

불변성이란 무엇인가?

Javascript에서 말하는 상태의 불변성은 메모리 영역에서 저장된 값을 변경할 수 없는 것을 의미합니다.

 

왜 React에서 불변성을 지켜야 하는가?

React에서 효율적인 상태 업데이트를 위해 얕은 비교를 통해 상태 업데이트를 수행합니다. 즉, 객체의 모든 프로퍼티를 일일히 비교하지 않고 객체의 참조 주소값이 변경되었는지만 확인하여 상태의 변화를 감지합니다. 상태 업데이트 효율성 측면에서 얕은 비교는 좋은 방법일 수 있지만, 해당 상태의 불변성을 지키지 않은 상태에서 얕은 비교가 진행된다면 상태가 업데이트 되었음에도 상태 변화를 감지하지 못해 렌더링이 되지않는 문제가 발생할 수 있습니다.

 

또한 React에서 참조 자료형 상태의 불변성이 지켜지지 않아 원본이 변경된다면, 해당 상태를 참조하고 있는 다른 부분에서 예상치 못한 사이드 이펙트가 발생할 수 있습니다.

 

정리하자면, 다음과 같은 이유로 React 상태 불변성이 지켜져야합니다.

1)효율적인 상태 업데이트

2)사이트 이펙트 방지 

 

 

어떻게 불변성을 지킬 수 있을까?

React 상태 불변성을 지키는 방법은 원본 데이터(상태)를 직접 수정하지 않고 원본 데이터의 복사본을 만들어서 값을 사용하는 것입니다.

setState([...state, newState]);
setState({...state, [key]: value});

 

구체적인 방법으로는 spread operator, map, filter, slice, reduce 등 새로운 배열을 반환하는 메서드를 활용하는 것입니다. 원본 데이터를 변경하는 splice, push 등의 사용은 지양합니다.

 

 

실제로 적용해봅시다!

import React, { useState } from "react";

const initialState = {
  firstName: "Paik",
  lastName: "Inbeen",
};

const ObjectUseState = () => {
  const [person, setPerson] = useState(initialState);

  const changeName = () => {
    person.firstName = "John";
    person.lastName = "Doe";
    setPerson(person);
  };

	console.log("ObjectUseState Render");

  return (
    <div>
      <button onClick={changeName}>
        {person.firstName} {person.lastName}
      </button>
    </div>
  );
};

export default ObjectUseState;

위와 같이 ObjectUseState 컴포넌트를 만들어 App.js에서 렌더링하면 아래와 같은 화면이 나타납니다.

 

초기 렌더링 모습 & 버튼을 클릭해도 아무런 변화가 없다

해당 버튼을 클릭해도 렌더링이 되지 않습니다. 이는 위에서 살펴본것처럼 React에서는 얕은 비교를 활용하여 상태의 변화를 감지하는데 얕은 복사를 사용하여 참조값을 변경한 경우, 해당 객체의 내용이 변경되었음에도 참조하고 있는 메모리 주소는 같은 주소를 가리키기 때문에 변경을 감지하지 못합니다. 그로 인해 리렌더링이 발생하지 않아 버튼을 클릭했음에도 아무런 변화가 생기지 않는 것입니다.

 

그렇다면 changeName 함수를 스프레드 연산자를 이용하여 깊은 복사를 통해 상태를 업데이트 해보겠습니다.

const changeName = () => {
    const newPerson = { ...person };
    newPerson.firstName = "John";
    newPerson.lastName = "Doe";
    setPerson(newPerson);
  };

 

초기 렌더링 모습
버튼 클릭했을때 업데이트된 모습

위와 같이 깊은 복사를 통해 해당 객체의 상태를 관리하게 되면 원본 객체의 상태의 불변성이 유지되고 상태가 업데이트 되면 이전 상태값과 업데이트 상태값이 달라지는것을 인지하기 때문에 리렌더링이 일어나고 적절하게 업데이트된 상태값을 화면에 보여주게 됩니다.

 

 

Reference