[참고: react.dev - Props and state are immutable]
위의 react 공식문서는 React에서 불변성이 중요한 이유와 왜 불변성을 유지해야하는지에 대해서 설명하고있습니다.
이 문서에서는 처음부터 전달하고 싶은 내용을 요약해서 제공합니다
A component’s props and state are immutable snapshots. Never mutate them directly
컴포넌트의 props와 state는 불변의 스냅샷이므로, 이를 직접 수정하지 말라고 합니다.
이 문장을 끊어서 보겠습니다.
컴포넌트의 props와 state는 불변의 스냅샷 / 따라서 직접 수정 x
핵심은 불변 / 스냅샷 / 직접 수정 x 라고 생각되어집니다.
스냅샷
우선 스냅샷에 대해서 알아보겠습니다.
state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render
상태를 `set`하는 것은 상태를 즉시 변경(업데이트)하는 것이 아니라, 단순히 리렌더링을 트리거한다고 말합니다.
이게 무슨 뜻일까요?
문서에는 아래처럼 예시를 들어서 설명합니다.
import { useState } from 'react';
export default function Form() {
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Hi!');
if (isSent) {
return <h1>Your message is on its way!</h1>;
}
return (
<form onSubmit={(e) => {
e.preventDefault();
setIsSent(true);
sendMessage(message);
}}>
<textarea
placeholder="Message"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Send</button>
</form>
);
}
function sendMessage(message) {
// 메시지 전송 로직...
}
위의 예시에서 "Send" 버튼을 누르면 `setIsSent(true)`에 의해 상태가 업데이트되고, UI가 재렌더링됩니다.
여기까지는 매우 일반적으로 알고있는 상황입니다.
하지만 내부적으로 React는 좀 더 세밀한 작업을 진행합니다.
setState(예: setIsSent(true))를 호출하면 현재 렌더링에서의 상태 변수를 즉시 변경하는 것이 아니라, React에게 해당 컴포넌트를 다시 렌더링하도록 요청합니다.
React는 내부적으로 각 렌더링 시점의 UI에 대한 스냅샷을 유지합니다. 컴포넌트가 렌더링될 때(즉, 컴포넌트 함수가 호출될 때), 그 함수는 JSX를 반환하며, 이는 그 시점의 UI를 나타내는 스냅샷 A입니다.
setState를 호출하면 React는 컴포넌트를 다시 렌더링하기로 예약하고, 다음 렌더링 시점에 컴포넌트 함수를 다시 호출합니다. 이때 새로운 상태 값을 기반으로 JSX를 반환하며, 이는 새로운 스냅샷 B가 됩니다.
이제 React는 스냅샷 A와 스냅샷 B를 비교합니다. 이 비교 과정은 Reconciliation(재조정)이라고 불리며, React는 이 과정을 통해 실제 DOM에서 어떤 부분이 변경되어야 하는지 계산합니다.
React는 변경 사항을 파악한 후, 실제 DOM에 변경된 부분만 효율적으로 업데이트합니다. 이를 통해 전체 UI를 다시 그리지 않고도 필요한 부분만 변경하여 성능을 최적화합니다.
State나 Props를 직접 수정하지 말 것
그럼 다시 최상단 인용문으로 돌아가서 컴포넌트의 props와 state는 불변의 스냅샷이므로라는 문장을 살펴보면,
React에서 컴포넌트가 렌더링될 때 받은 props와 state는 그 렌더링 동안에는 변경되지 않는 불변(immutable)의 값이며, 특정 시점의 상태를 나타내는 스냅샷이라는 것을 의미합니다.
즉, 컴포넌트 함수가 호출되어 JSX를 반환할 때, 해당 시점의 props와 state를 기반으로 UI를 그립니다. 이때의 props와 state는 그 렌더링 과정에서 변경되지 않으며, 렌더링이 완료될 때까지 고정된 값을 유지합니다.
상태를 변경하고 싶을 때 state 업데이트 함수를 호출하면, 현재의 state를 즉시 변경하는 것이 아니라 React에게 다음 렌더링에서 새로운 상태로 컴포넌트를 다시 렌더링하도록 요청하는 것입니다. 따라서, 현재 렌더링에서의 state와 props는 변경되지 않고 그대로 유지됩니다.
즉, props와 state는 렌더링중에는 불변의 고정된 값이라는 뜻에서 쓰인 문장으로 해석됩니다.
여기까지 정리하면 props와 state가 불변의 스냅샷이라는 것까지는 이해가 됩니다.
하지만 왜 직접 수정하지 말라는 걸까요?
결론부터 말하면 React가 상태의 변경을 인지하지 못할 수 있기 때문입니다.
리액트는 상태 업데이트에 state 업데이트 함수에 의존하고, 그 업데이트 함수에 의해 재렌더링이 트리거됩니다.
하지만 직접 값을 변경하면 재렌더링이 트리거 되지 않을 수 있고, 이는 위에서 강조한 props와 state는 렌더링중에는 불변의 고정된 값이라는 뜻 에 의해 오래된 UI를 보여줄 수 있습니다.
조금 프로그래밍적으로 풀어보면,
상태 변수 `user: {name: "asdf"}`가 존재 할 때, 직접 상태 값을 변경하면(user.name = "qwer"), React는 해당 변경을 인지하지 못할 수 있습니다.
이는 React가 상태 변경을 감지하기 위해서는 상태 변수 자체의 참조가 변경되어야 하기 때문입니다.
객체 내부의 속성만 변경되는 경우에는 객체의 참조가 그대로 유지되므로, React는 상태가 변경되었다고 판단하지 않습니다.
반면 state 업데이트 함수로 업데이트를 하면(setUser({ name: "qwer" }))를 호출하면 새로운 객체 { name: "qwer" }가 생성되고, user의 참조가 변경됩니다. React는 user의 참조가 변경되었음을 감지하고, 상태가 변경되었다고 판단하여 재렌더링을 트리거합니다.
따라서 React에서 상태를 업데이트할 때는 상태의 불변성을 지키고, 직접 업데이트 대신 state 업데이트 함수를 이용하여 재렌더링을 트리거해야합니다.
'React' 카테고리의 다른 글
[React] 공통 컴포넌트를 설계할 때 고려할 점 (2) | 2024.09.21 |
---|---|
[React] React Query 알아보기 01 - 왜 React Query를 사용해야하는지 (0) | 2024.09.18 |
[React] Suspense 이해하고 활용하기 01 - Suspense를 활용하면 스피너 지옥에서 탈출할 수 있습니다 (0) | 2024.09.18 |
[React] 사이드 이펙트는 렌더링에 영향을 주지 말아야합니다 (0) | 2024.09.16 |
[React] ref는 단순히 DOM 조작 용도가 아닙니다 (2) | 2024.09.15 |