State
State는 컴포넌트가 렌더 사이에 기억하는 값입니다. 버튼 클릭, 입력, 네트워크 응답 같은 변화가 생기면 state를 바꾸고, React는 그 새 값을 기준으로 다음 UI를 다시 계산합니다.
▶아키텍처 다이어그램
🔄 프로세스 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
정적인 props만으로는 사용자의 행동에 따라 달라지는 UI를 만들 수 없습니다. 열림과 닫힘, 현재 선택, 입력 중인 값처럼 시간이 지나며 바뀌는 정보를 어디엔가 붙잡아 두지 못하면 화면은 매번 처음 상태로 돌아가 버립니다.
초기 웹 UI는 DOM을 직접 읽고 쓰며 현재 상태를 여기저기 흩어 두는 경우가 많았습니다. 이런 방식은 작은 상호작용에는 버틸 수 있지만, 여러 이벤트가 얽히는 화면에서는 값이 언제 어떻게 바뀌었는지 추적하기 어려웠습니다. React는 state를 중심에 두고 UI를 현재 값의 함수처럼 설명하는 방향으로 이 복잡도를 줄였습니다.
컴포넌트는 렌더링 시점의 state 값을 읽어 JSX를 계산합니다. 이벤트 핸들러가 setState를 호출하면 React는 새 값을 예약하고, 다음 렌더에서 그 스냅샷을 사용해 UI를 다시 계산합니다. 즉 state를 바꾸는 것은 DOM을 직접 수정하는 명령이 아니라, 다음 화면을 다시 계산하라는 요청에 가깝습니다.
사용자 입력으로 state를 바꾸는 가장 기본적인 흐름
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 값: {count}</p>
<button onClick={() => setCount(count + 1)}>
증가
</button>
</div>
);
}버튼 클릭이 state를 바꾸고, 다음 렌더에서 새로운 값으로 UI가 다시 계산됩니다.
state는 현재 렌더에서 읽는 값이고 setter는 다음 렌더를 예약합니다
function SearchBox() {
const [query, setQuery] = useState("");
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setQuery(event.target.value);
}
return (
<>
<input value={query} onChange={handleChange} />
<p>검색어: {query}</p>
</>
);
}입력값을 state에 두면 화면은 항상 현재 state 스냅샷을 기준으로 그려집니다.
State와 props는 모두 렌더링에 영향을 주지만, props는 외부에서 들어오고 state는 내부에서 관리됩니다. Ref와도 자주 혼동되는데, ref는 값을 기억해도 렌더링을 다시 일으키지 않는 반면 state는 화면을 바꾸기 위한 값입니다. 폼 입력을 state로 연결하면 controlled pattern이 되고, DOM에서 직접 읽으면 ref 쪽에 더 가까워집니다.
실무에서 가장 중요한 질문은 state를 어디에 둘 것인가입니다. 너무 위에 두면 관련 없는 자식까지 함께 다시 렌더링되고, 너무 아래에 두면 여러 컴포넌트가 같은 값을 공유하기 어려워집니다. 값이 누구의 관심사인지 기준으로 가장 좁은 공통 조상까지 올리는 것이 기본 감각입니다.