[Day108] React - 불변성(Immutability) Part.1

Validator·2023년 11월 21일
post-thumbnail

JavaScript의 기본 데이터 구조

원시 타입과 객체 타입

  • 원시 타입(Primitive Types): Number, String, Boolean, null, undefined, Symbol과 같이 불변하는 가장 기본적인 데이터 타입이다. 원시 타입의 특징은 데이터가 변수에 직접 저장되며, 값이 복사될 때 실제 값이 복사된다는 것이다.
  • 객체 타입(Object Types): 객체, 배열, 함수 등 JavaScript에서는 객체를 포함한 모든 비원시 타입을 말한다. 객체 타입의 데이터는 변수에 참조값이 저장되며, 이 참조값을 통해 실제 데이터에 접근한다. 객체를 복사할 때 참조값이 복사되므로, 원본과 복사본 모두 같은 데이터를 가리킨다.

불변성(Immutability)

  • JavaScript에서 원시 타입은 자연스럽게 불변성을 가진다. 하지만 객체 타입은 가변적(mutable)이다. 이는 객체의 속성이나 배열의 요소를 쉽게 변경할 수 있다는 것을 의미한다.
  • 불변성을 유지하려면 객체를 변경하는 대신, 변경을 원하는 부분만을 다른 객체와 결합하여 새로운 객체를 생성해야 한다. 이런 방식은 데이터의 무결성을 보장하고, 여러 가지 버그를 방지하는 데 도움이 된다.

함수형 프로그래밍과 불변성

함수형 프로그래밍에서는 데이터의 불변성이 매우 중요하다. 이는 부작용(side effects)을 최소화하고, 프로그램의 예측 가능성을 높이는 데 기여한다.

순수 함수(Pure Functions)

  • 함수형 프로그래밍에서는 순수 함수의 개념이 중요하다. 순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부 상태를 변경하지 않는 함수이다.
  • 순수 함수는 불변성을 유지하는 데 도움을 주며, 테스트와 유지보수가 용이하다.

React와 State 관리

React에서 상태(state) 관리는 UI의 일관성과 성능 최적화에 중요한 역할을 한다.

useState와 useEffect

  • useState는 React 컴포넌트에서 상태를 관리하는 훅이다. useState를 사용하면 컴포넌트 내에서 상태를 선언하고, 이 상태를 업데이트할 수 있다.
  • useEffect는 컴포넌트가 렌더링될 때마다 특정 작업을 수행할 수 있게 해주는 훅이다. 예를 들어 API 호출, 구독 설정/해제 등이 이에 해당한다.

useReducer의 필요성

  • 복잡한 상태 로직이나 중첩된 객체를 관리할 때 useState만으로는 한계가 있다. 이럴 때 useReducer가 유용하다.
  • useReducer는 상태 업데이트 로직을 컴포넌트 외부에 작성할 수 있게 해준다. 이를 통해 상태 관리 로직을 더 깔끔하게 분리하고 테스트하기 쉬워진다.

Immutability(불변성)의 중요성

React에서 불변성을 유지하는 것은 성능 최적화와 버그 방지에 매우 중요하다. 특히, React의 useStateuseReducer를 사용할 때 불변성을 유지하면 리액트의 리렌더링 프로세스를 효율적으로 관리할 수 있다.

불변성의 이점

  1. 예측 가능한 코드: 데이터가 예측 가능하게 변화한다면, 버그를 쉽게 추적하고 해결할 수 있다.
  2. 리액트 최적화: React는 참조 변경을 기반으로 컴포넌트의 리렌더링을 결정한다. 불변성을 유지하면 React가 더 효율적으로 UI를 업데이트할 수 있다.

불변성 유지 방법

  • 객체나 배열을 직접 수정하지 말고, 새로운 객체나 배열을 만들어서 업데이트한다.
  • JavaScript의 전개 연산자(spread operator)나 Object.assign()을 사용하여 새로운 객체나 배열을 생성할 수 있다.

React의 useReducer 이해

useReducer는 React의 상태 관리 훅 중 하나로, 주로 복잡한 상태 로직을 다룰 때 사용한다. useState와 비교했을 때, useReducer는 보다 구조화된 방식으로 상태를 관리할 수 있게 해주며, 특히 여러 하위 값이 있는 복잡한 상태 객체를 다룰 때 유리하다.

useReducer의 기본 구조

useReducer는 두 가지 주요 요소로 구성된다: reducer 함수와 초기 상태(initial state)이다.

  1. Reducer 함수: 이 함수는 현재 상태와 업데이트를 위한 액션을 인자로 받아, 새로운 상태를 반환한다. 이 함수는 순수 함수로 작성되어야 하며, 이전 상태를 직접 변경하지 않고 새로운 상태 객체를 생성하여 반환해야 한다.

  2. 초기 상태(Initial State): useReducer를 사용할 때 초기 상태를 설정할 수 있다. 이 상태는 reducer 함수에 의해 관리된다.

useReducer 사용 예시

import React, { useReducer } from 'react';

// 초기 상태 정의
const initialState = {
  count: 0
};

// reducer 함수 정의
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'decrement':
      return { ...state, count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

위 예시에서 reducer 함수는 현재 상태(state)와 액션 객체(action)를 받아 새로운 상태를 반환한다. 이 때, 상태를 직접 변경하지 않고, 새로운 객체를 생성하여 반환하는 것이 중요하다. 이는 불변성을 유지하는 핵심이다.


불변성을 유지하는 방법 !!

객체 불변성 유지

JavaScript에서 객체의 불변성을 유지하는 방법은 여러 가지가 있다. 가장 간단한 방법은 전개 연산자(Spread Operator)를 사용하는 것이다.

예시:

const object = { a: 1, b: 2 };
const newObject = { ...object, b: 3 }; // { a: 1, b: 3 }

여기서 newObjectobject의 복사본이며, b의 값만 변경되었다. 이렇게 하면 원본 객체는 변경되지 않는다.

배열 불변성 유지

배열의 경우에도 전개 연산자를 사용하여 불변성을 유지할 수 있다.

예시:

const array = [1, 2, 3];
const newArray = [...array, 4]; // [1, 2, 3, 4]

newArrayarray의 복사본이며, 새로운 요소가 추가되었다. 이 경우에도 원본 배열은 변경되지 않는다.

정리!

React에서 useReducer를 사용하면 복잡한 상태 관리를 보다 효과적으로 할 수 있다. 이 과정에서 불변성을 유지하는 것은 매우 중요하며, 이를 위해 순수 함수와 전개 연산자 등을 활용할 수 있다. 불변성을 유지하면 애플리케이션의 성능을 향상시키고, 예측 가능한 코드를 작성하는 데 도움이 된다.

0개의 댓글