State는 컴포넌트 내부에서 관리하는 변경 가능한 데이터입니다. State가 바뀌면 React는 해당 컴포넌트를 다시 렌더링하여 UI를 최신 상태로 갱신합니다.
사용자 입력, 서버 응답, 타이머 등 시간이 지나면서 변하는 데이터는 State로 관리합니다.
함수형 컴포넌트에서 State를 사용하려면 useState Hook을 사용합니다.
import { useState } from 'react';
function Counter() {
// [현재값, 변경함수] = useState(초기값)
const [count, setCount] = useState(0);
return (
<div>
<p>현재 값: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
<button onClick={() => setCount(count - 1)}>감소</button>
</div>
);
}
useState는 두 개의 요소를 가진 배열을 반환합니다. 첫 번째는 현재 상태 값이고, 두 번째는 상태를 변경하는 함수입니다. 배열 구조 분해를 활용하여 이름을 자유롭게 지정할 수 있습니다.
초기값으로 함수를 전달하면 컴포넌트가 처음 마운트될 때만 실행됩니다. 계산 비용이 큰 초기값을 설정할 때 유용합니다.
// 매 렌더링마다 실행됨 (비효율)
const [data, setData] = useState(expensiveComputation());
// 최초 마운트 시에만 실행됨 (효율적)
const [data, setData] = useState(() => expensiveComputation());
State는 직접 수정해서는 안 됩니다. 반드시 setState 함수를 통해 새 값을 반환해야 합니다. 이를 불변 업데이트(Immutable Update)라고 합니다.
// 금지: state를 직접 수정
const [user, setUser] = useState({ name: '홍길동', age: 30 });
user.age = 31; // React가 변경을 감지하지 못함
// 올바른 방법: 새 객체를 생성하여 전달
setUser({ ...user, age: 31 });
React가 변경을 감지하는 방법은 이전 참조와 새 참조를 비교(===)하는 것입니다. 객체를 직접 수정하면 참조가 바뀌지 않으므로 React는 변경이 없다고 판단하여 리렌더링이 발생하지 않습니다.
배열 State의 경우도 마찬가지입니다.
const [items, setItems] = useState([]);
// 금지
items.push(newItem);
setItems(items);
// 올바른 방법: 새 배열 생성
setItems([...items, newItem]);
// 항목 제거
setItems(items.filter(item => item.id !== targetId));
// 항목 수정
setItems(items.map(item =>
item.id === targetId ? { ...item, done: true } : item
));
setState 함수가 호출되면 React는 다음 두 조건을 확인합니다.
Object.is로 비교)const [count, setCount] = useState(0);
// count가 이미 0이면 리렌더링 발생 안 함
setCount(0);
// 객체는 참조가 다르면 항상 리렌더링 발생
setUser({ ...user }); // 내용이 같아도 새 객체이므로 리렌더링 발생
여러 State 업데이트가 동일한 이벤트 핸들러 안에서 발생하면 React는 이를 일괄 처리(Batching)하여 렌더링을 한 번만 수행합니다.
function handleClick() {
setCount(c => c + 1);
setName('홍길동');
// 위 두 업데이트는 하나의 리렌더링으로 처리됨
}
| 구분 | Props | State |
|---|---|---|
| 소유자 | 부모 컴포넌트 | 해당 컴포넌트 |
| 변경 가능 여부 | 읽기 전용 | 변경 가능 |
| 변경 주체 | 부모 | 해당 컴포넌트 |
| 용도 | 외부에서 데이터 주입 | 내부 동적 데이터 관리 |
어떤 데이터를 State로 관리해야 할지 판단할 때는 다음 질문을 해봅니다. "이 데이터가 변하는가? 변했을 때 UI가 달라지는가?" 두 질문 모두 "예"라면 State가 적합합니다.
State는 컴포넌트를 동적으로 만드는 핵심 메커니즘입니다. useState로 선언하고, 반드시 setState 함수를 통해 업데이트해야 합니다. 불변 업데이트 원칙을 지켜야 React가 변경을 정확히 감지하고 리렌더링을 올바르게 수행합니다.