제어 컴포넌트(Controlled Component)는 입력값의 진실 원천(Single Source of Truth)이 React state이고, 비제어 컴포넌트(Uncontrolled Component)는 입력값을 DOM(Document Object Model)이 직접 들고 있어서 필요할 때 ref로 꺼내 읽는다.
// 제어: 키 입력마다 state → 렌더
const [email, setEmail] = useState('')
<input value={email} onChange={e => setEmail(e.target.value)} />
// 비제어: DOM이 보관, 제출 시 읽기
const ref = useRef(null)
<input defaultValue="" ref={ref} />| 제어 | 비제어 | |
|---|---|---|
| 진실 원천 | state | DOM |
| 렌더 | 입력마다 | 거의 없음 |
| 실시간 검증·포맷 | 쉬움 | 따로 처리 |
| 코드량 | 많음 | 적음, HTML(HyperText Markup Language) 폼과 잘 맞음 |
- 제어 방식은 React의 단방향 데이터 흐름(One-Way Data Flow)에 맞아 예측하기 쉽다. 입력 중 검증, 마스킹, 다른 필드에 따른 조건부 렌더가 필요하면 이쪽이다(React 상태 갱신)
- 비제어 방식은 리렌더가 적다. 필드가 많은 폼에서 성능이 낫다
- react-hook-form이 비제어 기반인 이유도 이것이다. 입력할 때마다 렌더하지 않고, 필드 초기값을 state로 들 필요가 없으며, 네이티브 폼 동작을 활용한다. 필요하면 일부 필드만 제어 방식처럼 쓸 수 있다
- 같은 폼 안에서 둘을 섞으면 값이 어디에 있는지 헷갈린다. 하나의 컴포넌트가 제어였다가 비제어로 바뀌면 React가 경고한다
- 서버로 바로 제출하는 폼은 form action과 비제어 입력이 자연스럽게 맞는다