2025-05-14
useState는 내부 상태를 위한 훅이다.useState 값은 자동으로 바뀌지 않는다.useEffect로 따로 감지해서 수동으로 동기화해야 한다.리액트를 배우는 초반에 useState는 내부 상태를 위한 훅이라는 사실을 자연스럽게 받아들였고, 이후로도 당연한 전제로 개발을 이어왔습니다. 그러다 이 동작 방식을 의도적으로 활용해야 하는 상황을 직접 마주하고 나서야, React가 이렇게 동작하도록 만든 이유와 구조적 의도가 더 분명하게 이해되었습니다.
예제를 하나 보겠습니다.
import { useState } from "react";
export function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((c) => c + 1)}>부모 값 증가</button>
<Child initialValue={count} />
</>
);
}
function Child({ initialValue }: { initialValue: number }) {
const [value, setValue] = useState(initialValue);
return (
<>
<p>부모에서 받은 값: {initialValue}</p>
<p>내부 상태값: {value}</p>
<button onClick={() => setValue((v) => v + 1)}>내 상태 증가</button>
</>
);
}
이 코드에서 Parent의 count 값이 바뀌면 initialValue도 바뀌겠지만,
자식 컴포넌트의 useState(initialValue)로 관리되는 value는 바뀌지 않습니다.
왜냐하면 useState는 컴포넌트가 다시 렌더링되더라도 초기값을 다시 적용하지 않기 때문입니다.
방법은 두 가지입니다:
useEffect로 직접 감지해서 반영하기useEffect(() => {
setValue(initialValue);
}, [initialValue]);
이 방식은 명시적이고 예측 가능하지만, 상황이 반복되거나 로직이 많아질수록 코드가 점점 복잡해질 수 있습니다.
key를 이용해 컴포넌트를 강제로 재마운트<Child key={count} initialValue={count} />
React는 key가 바뀌면 해당 컴포넌트를 완전히 unmount → remount 합니다.
이렇게 하면 useState(initialValue)가 새로운 초기값으로 다시 실행됩니다.
useState는 한 번 초기값을 주면, 이후 외부에서 값이 바뀌어도 내부 상태에는 아무 영향이 없습니다. 이 동작 방식에 너무 익숙해져 있다 보니, useOptimistic을 마주했을 때 자연스럽게 같은 원리로 이해하려 했습니다. 하지만 그건 오해였습니다.
useOptimistic은 겉보기에는 상태처럼 보이지만, 실제로는 외부에서 전달된 값을 따라 움직입니다. 특히 pending 상태가 아닐 때는, 단순히 첫 번째 인자로 전달된 값을 그대로 반환하죠. 이처럼 외부 값에 동기화되는 흐름은 기존의 상태 관리 방식과는 다른 결을 가지고 있었고, 오히려 그 낯섦이 React의 상태 처리 방식을 다시 돌아보게 만들었습니다.
다음 포스팅에서는 사이드 프로젝트 Pick Road 를 진행하며, useOptimistic을 실제로 어떻게 활용했는지 구체적인 사례를 중심으로 풀어보려 합니다.