2025-06-17
리액트 공식 문서를 다시 한번 살펴보던 중, 상태(state)가 보존되거나 초기화되는 방식에 대해 새로운 시각을 갖게 된 섹션이 있었습니다. 평소에도 상태가 예상과 다르게 초기화되거나 유지되는 경험을 한 적은 있었지만, 정확히 어떤 원리로 동작하는지에 대해선 깊게 생각해본 적이 없었습니다.
이번 문서를 통해 React가 상태를 '렌더 트리(render tree)'의 위치 기준으로 관리한다는 사실을 제대로 이해할 수 있었습니다.
React 공식 문서에서는 이 개념을 다음과 같이 설명하고 있습니다.
처음엔 이 설명이 다소 추상적으로 느껴졌지만, 문서의 예제와 그림이 워낙 잘 되어 있어서 이해하는 데 큰 어려움은 없었습니다.
export default function App() {
const [isHighlight, setIsHighlight] = useState(false);
return (
<>
<div
className="card"
style={{
border: isHighlight ? "2px solid yellow" : "2px solid transparent",
}}
>
{isHighlight ? <Counter /> : <Counter />}
</div>
<div>
<button onClick={() => setIsHighlight(!isHighlight)}>
{isHighlight ? "Turn off" : "Turn on"} Highlight
</button>
</div>
</>
);
}
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
코드상에서는 isHighlight 값에 따라 <Counter /> 컴포넌트가 바뀌는것 처럼 보이지만, 실제로는 같은 위치에 같은 컴포넌트가 렌더링되므로 <Counter /> 컴포넌트의 상태가 유지됩니다. (버튼을 눌러서 카운트값을 증가시킨후, isHighlight를 변경해도 카운트 값은 초기화되지 않습니다.)

export default function App() {
const [isHighlight, setIsHighlight] = useState(false);
if (isHighlight) {
return (
<>
<div
className="card"
style={{
border: "2px solid yellow",
}}
>
<Counter />
</div>
<div>
<button onClick={() => setIsHighlight(!isHighlight)}>
Turn off Highlight
</button>
</div>
</>
);
}
return (
<>
<div
className="card"
style={{
border: "2px solid transparent",
}}
>
<Counter />
</div>
<div>
<button onClick={() => setIsHighlight(!isHighlight)}>
Turn onHighlight
</button>
</div>
</>
);
}
JSX가 완전히 두 개로 나뉘어 있지만, 두 경우 모두 Counter가 동일한 UI 트리의 위치에 렌더링되므로 상태가 유지됩니다.

프로젝트를 하다 보면 같은 위치에 렌더링되는 컴포넌트의 상태를 강제로 초기화하기 위해 key를 사용하는 경우가 있었습니다. 이번 문서를 통해, 왜 그런 방식이 필요한지, React가 상태를 어떤 기준으로 보존하는지에 대해 더 명확하게 이해할 수 있었습니다.
React의 상태가 단순히 컴포넌트에 묶여 있는 것이 아니라, 렌더 트리의 위치 기준으로 연결되어 있다는 점은 상태 관리에서 중요한 관점이라는 걸 다시 한번 느꼈습니다.