[React] 리액트의 상태가 불변성을 유지하는 이유

jiny·2025년 1월 25일

기술 면접

목록 보기
39/78

🗣️ 리액트의 상태가 불변성을 유지하는 이유는 무엇일까요?

  • 의도: 무의식적으로 사용하고 있던 개념에 대해 확인하는 질문

  • 나의 답안

    리액트에서 상태가 불변성을 유지하는 이유는 상태 변경 감지효율적인 렌더링을 위해서입니다.

    리액트는 상태가 바뀌면 컴포넌트를 다시 렌더링하는데,
    이때 '바뀌었는지'를 판단하기 위해 얕은 비교(shallow comparison)를 사용합니다.
    즉, 객체의 내용 전체를 깊게 비교하지 않고 참조(reference)가 달라졌는지만 확인합니다.

    그래서 상태를 직접 수정하면 참조가 그대로 유지되어 리액트는 변경이 없다고 판단해서
    렌더링이 일어나지 않거나, 예상과 다른 UI가 표시되는 버그가 발생할 수 있습니다.
    반대로, 불변성을 지켜 새로운 객체나 배열을 만들어 상태를 갱신하면
    참조가 달라지기 때문에 리액트가 정확하게 변경을 감지하고 필요한 부분만 다시 렌더링할 수 있습니다.

  • 주어진 답안 (모범 답안)

    리액트는 불변성을 유지하도록 설계되었습니다.
    그래서 잘 아시다시피 상태를 변경하려면 직접 변경하는 것이 아니라, 새로운 객체를 만들어서 할당해주어야 합니다.
    이러한 불변성을 유지하는 것에는 여러 이유가 있는데 그 중 대표적으로 불변성 덕분에 리액트는 상태가 언제 어떻게 변경되었는지 추적하기가 쉬워집니다.

    만약 직접 변경한다면 무엇이 언제 어떻게 변경되었는지 추적하기 쉽지 않을 것입니다.
    리덕스나 리액트의 개발자 도구를 보면 상태가 어떻게 변화되었는지 찾아보는 게 굉장히 유용한데, 이게 다 불변성 덕분입니다.

    다만 불변성을 유지하는 게 개발자 입장에서는 어려울 수도 있는 일이라 immer 같은 라이브러리를 이용해 대신 불변성을 유지할 수 있도록 위임하는 일도 가끔 있습니다.
    오히려 리덕스 툴킷에는 내장되어 있는 기능이기도 하니 불변성을 유지하다 보니 코드가 어지러워진다고 생각하면 도입을 적극 고려해야 한다고 생각합니다.


📝 개념 정리

🌟 React의 상태 불변성이란?

  • React의 상태 불변성(immutability)은 상태를 직접 수정하지 않고, 상태를 복사하여 새로운 상태를 생성한 뒤 이를 업데이트하는 방식을 의미한다.
  • React에서 상태는 컴포넌트의 UI와 밀접하게 연결되어 있으며, 상태가 변경되었음을 React에서 명확히 알려야 UI가 올바르게 업데이트된다.
  • 상태 불변성을 유지하면 React의 상태 변경을 감지하고 효율적으로 렌더링을 처리할 수 있다.

🌟 왜 불변성을 유지해야 할까?

React에서 상태 불변성을 유지해야 하는 주된 이유는 성능 최적화에측 가능한 코드 작성 때문이다. 구체적으로 보면 다음과 같다.

  1. React의 상태 비교 방식
    React는 상태 변경 여부를 비교할 때 얕은 비교(Shallow Comparison)를 사용한다.
    즉, 참조값만 비교하여 상태가 변경되었는지 판단한다.
    • 상태가 불변성을 유지하면 이전 상태와 새로운 상태의 참조값이 달라지므로, React는 상태가 변경되었음을 쉽게 감지할 수 있다.
    • 상태를 직접 수정하면 참조값이 그대로 유지되어 React가 변경을 감지하지 못할 수 있다.
  1. 효율적인 렌더링
    React는 상태 변경이 발생했을 때 Virtual DOM을 사용하여 변경된 부분만 다시 렌더링한다.
    상태가 불변성을 유지하면 변경된 부분을 빠르게 감지할 수 있어 성능이 향상된다.
  1. 예측 가능성과 디버깅의 용이성
    불변성을 유지하면 상태가 변경되는 과정을 쉽게 추적할 수 있다.
    상태가 직접 수정되면 디버깅이 어려워질 수 있으며, 버그가 발생할 가능성이 높아진다.

🌟 상태 불변성 유지 방법

React에서 상태를 불변으로 유지하려면, 기존 상태를 직접 변경하지 않고 새로운 상태 객체를 생성해야 한다. 이를 구현하는 방법은 다음과 같다.

  1. 객체의 상태 업데이트
    객체를 업데이트할 때는 기존 객체를 복사한 뒤, 변경된 속성을 덮어쓴다.

    const [state, setState] = useState({name: "John", age: 25});
    
    // 상태 업데이트 시
    setState({
      ...state, // 기존 상태를 얕게 복사
      age: 26, // 복사된 객체에서 특정 속성만 업데이트
    })
  2. 배열의 상태 업데이트
    배열을 업데이트할 때는 map, filter, concat 등의 불변성을 유지하는 메서드를 사용한다.

    • 배열에 새로운 항목 추가

      const [items, setItems] = useState([1, 2, 3]);
      
      setItems([...items, 4]); // 기존 배열을 복사하고 새 항목 추가
    • 배열에서 항목 제거

      setItems(items.filter(item => item !== 2)); // 조건에 맞는 항목 제거
    • 배열의 특정 항목 수정

      setItems(items.map(item => (item === 2 ? 20 : item))); // 조건에 맞는 항목 변경
  3. Immer 라이브러리 사용
    Immer는 상태를 불변으로 유지하는 로직을 단순화해주는 라이브러리이다.
    Immer를 사용하면 기존 상태를 직접 수정하는 것처럼 작성하더라도 내부적으로 불변성을 유지한다.

    npm install immer
    import produce from "immer";
    
    const [state, setState] = useState({ name: "John", age: 25 });
    
    setState(produce(state, draft => {
      draft.age = 26; // 상태를 직접 변경하는 것처럼 작성
    }))
    • 장점: 코드가 간결해지고 가독성이 좋아진다.
    • 단점: 추가적인 라이브러리를 설치해야 한다.

🌟 불변성을 깨뜨리는 사례

React에서 상태를 직접 수정하면 불변성이 깨지고, 예상치 못한 문제가 발생할 수 있다.
아래는 불변성을 깨뜨리는 사례와 이를 수정하는 방법이다.

  1. 객체를 직접 수정
    state.age = 26; // 직접 수정 (잘못된 방법)
    setState(state); // React는 변경을 감지하지 못함
    수정된 코드
    setState({
      ...state,
      age: 26,
    });
  1. 배열을 직접 수정
    items.push(4); // 배열 직접 수정 (잘못된 방법)
    setItems(items); // React는 변경을 감지하지 못함
    수정된 코드
    setItems([...items, 4]);

🌟 상태 불변성의 이점

  1. React의 성능 최적화
    • 상태 변경을 효율적으로 감지하여 필요한 컴포넌트만 다시 렌더링한다.
    • Virtual DOM 비교를 최적화한다.
  1. 코드 가독성 및 유지보수성 향상
    • 불변성을 유지하면 상태 관리 코드가 예측 가능하고 명확해진다.
    • 디버깅이 용이하다.
  1. 타임 트래블 디버깅 가능
    • 상태가 불변성을 유지하면, Redux DevTools 같은 도구로 상태 변경 이력을 추적하거나 상태를 과거로 되돌리는 기능을 구현할 수 있다.

🌟 결론

React의 상태 불변성은 상태 관리에서 핵심적인 개념이다.
불변성을 유지하면 성능과 코드의 예측 가능성이 향상되고, 디버깅이 쉬워진다.
React는 불변성을 보장하지 않으므로, 개발자가 직접 상태를 불변하게 관리해야 하며, 이를 위해 Spread 연산자, map() 함수 등의 내장 메서드, 또는 Immer 같은 도구를 활용하는 것이 좋다.

0개의 댓글