리엑트의 불변성

2024. 4. 24. 21:49지식&개념

리엑트의 가장 중요한 특징이라고 하면 불변성일 것이다. 
아래의 궁금증을 가지며 글을 작성해본다.
리엑트는 왜 불변성을 가질까?
불변성을 지켜 어떤 이점을 가져갈 수 있고, 그에 따른 단점이 무엇일까?


불변성

 

불변성은 메모리에 있는 값을 변경할 수 없는 것을 말한다.

하지만 우리는 변수의 값을 곧 잘 변경했었다. 편의상 변경이라고는 하지만 내부적으로는 변하지 않는다.

변수는 기존 값을 참조하고, 변수의 값을 변경시 새로운 값이 생성되고 변수는 새로 생성된 값을 참조하게 된다. 아무런 참조가 없는 값은 가비지 콜렉터가 처리하게 된다.

 

  •  원시 데이터(string, number, boolean, null, undefined, Symbol) : 불변성이 있다.
    •  원시 데이터는 수정시, 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장 공간에 새로운 값을 저장한다.
  •  원시 데이터가 아닌 데이터(Object 타입 : 객체, 배열, 함수 등) : 불변성이 없다.
    •  원시 데이터가 아닌 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장 공간의 값 자체를 변경한다.

프로그래밍 및 데이터 구조의 맥락에서 불변성은 일단 인스턴스화 되면 내부 상태에 대한 수정을 방지하는 객체 또는 데이터 구조의 속성을 나타낸다.

 

기존 객체를 수정하는 대신 해당 상태를 변경하면 원하는 변경 사항이 포함된 새 객체가 생성된다.

 

 


불변 패러다임

 

불변 데이터는 함수형 프로그래밍에서 파생된 개념이다.

간단히 불변 객체는 한번 생성되면 변경할 수 없는 객체이다.

javascript 및 react 의 맥락에서 이는 기존 객체를 수정하는 대신 변경을 원할 때마다 상태의 새 버전을 생성한다는 의미이다.

 


React 불변성의 이점

 

javascript 는 기본적으로 변경 가능한 데이터 구조를 허용한다. 이는 변수가 시간이 지남에 따라 값을 변경할 수 있음을 의미한다.

가변성은 편리할 수 있지만 코드에 복잡성과 예상치 못한 동작이 발생할 수 있다.

 

반면 불변성은 예측 가능하고 안정적인 프로그래밍 스타일을 촉진한다. 데이터를 변경할 수 없으면 예기치 않게 변경되지 않으므로 버그가 줄어들고 디버깅이 쉬워진다.

 

react 내에서 불변성의 주요 이점은 조정 프로세스, 즉 DOM 을 효율적으로 업데이트하는 react의 방식에 있다.

이전 상태와 새 상태를 비교하여 react 는 어떤 변경이 필요한지 결정한다. 

불변 데이터를 사용하면 객체를 심층적으로 비교하는 대신 이전 참조와 새 참조가 다른지 간단하게 확인할 수 있어 비교가 훨씬 빠르고 효율적이다.

 

  1.  일관성 : 불변 데이터 구조는 수명 내내 일관된 상태로 유지된다. 일단 생성되면 예상치 못한 수정에 대한 걱정 없이 프로그램의 여러 부분에서 안전하게 공유할 수 있다.
  2.  예측 가능성 : 불변성은 실수로 객체의 상태를 변경할 수 없기 때문에 코드를 예측할 수 있다. 이렇게 하면 버그 가능성이 줄어들고 코드에 대해 더 쉽게 추론할 수 있다.
  3.  동시성 : 불변 데이터 구조는 잠금 및 동기화 메커니즘이 필요 없기 때문에 동시 또는 다중 스레드 프로그래밍에 적합하다. 데이터는 변경될 수 없으므로 여러 스레드가 동시에 안전하게 액세스 할 수 있다.
  4.  함수형 프로그래밍 : 불변성은 함수형 프로그래밍의 기본 개념이다. 함수형 프로그래밍 언어와 패러다임은 불변 데이터 작업을 강조하며, 이는 선언적이고 순수한 함수로 이어진다.
  5.  성능 : 새로운 객체를 생성하는 것이 비효율적으로 보일 수 있지만 최신 프로그래밍 언어와 라이브러리는 새로운 불변 객체를 생성하는 프로세스를 최적화한다. 대부분의 불변 데이터 구조는 복사와 같은 특정 작업의 경우 변경 가능한 데이터 구조만큼 효율적일 수 있다.

 


불변성의 단점

 

  1.  메모리 소비 : 변경이 이루어 질 때마다 새로운 데이터 복사본을 생성하는 것은 대규모 데이터 세트의 경우 리소스 집약적일 수 있다. 때문에 불변 데이터 구조에는 더 많은 메모리가 필요한 경우가 많다.
  2.  성능 오버헤드 : 일부 시나리오에서는 새로운 데이터 복사본을 생성하는데 따른 성능 오버헤드가 불변성의 이점보다 클 수 있다. 실시간 애플리케이션이나 메모리가 제한된 상황에서는 불변성이 최선이 아닐 수 있다.
  3.  업데이트의 복잡성 : 변경 불가능한 데이터를 업데이트하는 것은 변경 가능한 데이터를 제자리에 수정하는 것 보다 덜 직관적일 수 있다. 어떤 경우에는 사소한 업데이트를 위해 완전히 새로운 데이터 구조를 생성하면 코드가 복잡해질 수 있다.
  4.  호환성 : 변경 불가능한 데이터를 기존 코드베이스, 변경 가능한 데이터에 의존하는 코드베이스에 통합하는 것은 어려울 수 있다. 이 전환에는 불변성을 수용하기 위한 리펙토링 및 조정이 필요할 수 있다.

변경가능과 불변

 

// 배열 수정(변경 가능)
const mutableArr = [1, 2, 3];
mutableArr.push(4);

// 새 배열 생성(불변)
const immutableArr = [1, 2, 3];
const newImmutableArr = [...immutableArr, 4];

 

이처럼 불변성은 변하지 않고 일관되며 예측 가능한 데이터 구조의 생성을 촉진한다.

 


동작

 

1. 불변 데이터

 

react 는 객체를 포함한 불변 데이터 구조의 사용을 권장한다. react 에서 객체를 업데이트 할 때 기존 객체를 수정하는 대신 새 객체를 생성해야 한다. react 는 구성 요소를 다시 렌더링할 시기를 결정하기 위해 참조 동일성에 의존하기 때문에 중요하다. 객체를 제자리에서 수정하면 react 가 변경 사항을 감지하지 못하고 다시 렌더링을 트리거하지 않을 수 있다.

 

// 안좋음 : 상태 객체를 직접 변경
this.state.user.name = 'New Name';

// 좋음 : 업데이트 된 데이터를 사용하여 새 객체 생성
const updateUser = { ...this.state.user, name: 'New Name' };
this.setState({ user: updateUser })

 

2. useState 훅 

 

기능적 구성 요소에서 useState 훅을 사용할 때 react 는 업데이트 된 값이 포함된 새 개체를 전달하여 상태를 업데이트 하는 방법을 제공한다. react 는 변경사항을 기존 상태 객체에 병합한다.

 

const [user, setUser] = useState({ name: 'yeon', age: 30 });

// 새 객체로 상태 업데이트
setUser({ ...user, name: 'New Name' });

 

3. 객체 참조 평등

 

react는 객체 참조 평등을 사용해 구성 요소를 다시 렌더링할지 여부를 결정한다. 객체의 속성이 변경될 때 구성 요소가 다시 렌더링 되도록 하려면 해당 변경 사항이 포함된 새 객체를 만들어야 한다. 이는 객체를 하위 구성 요소에 소품으로 전달할 때 중요하다.

 

// ParentComponent.jsx
const [user, setUser] = useState({ name: 'yeon', age: 30 });

// ChildComponent.jsx
const ChildComponent = ({user}) => {
  // 사용자 개체 참조가 변경되지 않으면 이 구성요소는 다시 렌더링되지 않는다.
  // 구성 요소에서 user.name 및 user.age 를 사용해야 한다.
}

 

4. useEffect 훅

 

useEffect 훅을 사용할 때 종속성을 지정하여 효과가 실행되어야 하는 시기를 결정할 수 있다.

개체 참조가 종속성 배열에 포함되어 있으면 개체 참조가 변경될 때마다 효과가 실행된다.

 

useEffect(() => {
  // 이 효과는 user 개체 참조가 변경되면 실행된다.
  // user가 변경되면 작업을 수행
}, [user]);

 

5. Context API

 

상태 관리를 위해 react 의 context API 를 사용하는 경우 해당 컨텍스트를 소비하는 구성 요소에서 다시 렌더링을 트리거하기 위해 컨텍스트 값을 업데이트 할 때 새 객체를 생성하는 것이 중요하다.

 

const MyContext = createContext();

const MyProvider = ({ children }) => {
	const [value, setValue] = useState({ data: 'initial' });
    
    const updateValue = () => {
    	// 컨텍스트 값을 업데이트 하기 위해 새 객체를 생성한다.
        setValue({ ...value, data: 'updated' });
    };
    
    return (
    	<MyContext.Provider value={{ value, updateValue }}>
        	{ children }
        </MyContext.Provider>
    );
};

 

요약하자면 react 의 객체 참조는 불변으로 처리되어야 하며, react 애플리케이션에서 적절한 렌더링 및 상태관리를 보장하기 위해 필요할 때 업데이트 된 데이터로 새 객체를 생성해야 한다.

객체를 직접 변경하면 예상치 못한 동작과 버그가 발생할 수 있다.

'지식&개념' 카테고리의 다른 글

자바스크립트의 역사  (0) 2024.04.23
토큰에 대한 고민  (0) 2024.04.22
UUID (Universally Unique IDentifier)  (1) 2024.04.19
단방향 데이터 바인딩 양방향 데이터 바인딩  (0) 2024.04.18
Angular vs React vs Vue  (0) 2024.04.16