React에서 폼(Form) 요소를 다루는 방식은 크게 두 가지로 나뉩니다. 데이터를 React 상태(state)로 관리하는 Controlled Component와, DOM이 직접 데이터를 보관하고 필요할 때 ref로 읽어오는 Uncontrolled Component입니다. 두 방식은 철학이 다르며, 상황에 따라 적합한 선택이 달라집니다.
Controlled Component는 입력값을 React 상태로 관리합니다. 사용자가 입력할 때마다 이벤트 핸들러가 호출되고, 상태가 업데이트되며, 업데이트된 상태가 다시 입력 요소의 값으로 반영됩니다. React가 폼 데이터의 "단일 진실 공급원(Single Source of Truth)"이 됩니다.
단일 진실 공급원(Single Source of Truth): 데이터가 오직 한 곳에서만 관리되어 일관성을 보장하는 설계 원칙.
function ControlledForm() {
const [value, setValue] = useState("");
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
사용자 입력
│
▼
onChange 이벤트
│
▼
setState 호출
│
▼
리렌더링 → input value 갱신
Controlled Component의 장점은 React 상태와 UI가 항상 동기화된다는 것입니다. 입력값을 실시간으로 검증하거나, 조건에 따라 입력을 차단하거나, 다른 컴포넌트와 상태를 공유하는 작업이 자연스럽게 처리됩니다. 단점은 모든 키 입력마다 상태 업데이트와 리렌더링이 발생한다는 것입니다. 폼 필드가 많고 입력 빈도가 높은 경우 성능에 영향을 줄 수 있습니다.
Uncontrolled Component는 폼 데이터를 DOM이 직접 보관합니다. React는 초기값만 설정하고(defaultValue), 실제 값은 필요한 시점에 ref를 통해 DOM에서 직접 읽어옵니다.
ref: React에서 DOM 요소나 컴포넌트 인스턴스에 직접 접근하기 위한 참조 객체.
function UncontrolledForm() {
const inputRef = useRef(null);
function handleSubmit() {
console.log(inputRef.current.value);
}
return (
<>
<input ref={inputRef} defaultValue="" />
<button onClick={handleSubmit}>제출</button>
</>
);
}
사용자 입력
│
▼
DOM이 값 보관
(React 관여 없음)
│
▼ (제출 시점)
ref.current.value로 읽기
Uncontrolled Component의 장점은 키 입력마다 리렌더링이 발생하지 않아 성능 부담이 적다는 것입니다. 또한 파일 업로드(<input type="file">)처럼 React 상태로 다루기 어려운 요소를 처리할 때 필수적입니다. 단점은 실시간 검증이나 조건부 비활성화처럼 입력 흐름을 제어하는 작업이 까다롭다는 것입니다.
| 항목 | Controlled | Uncontrolled |
|---|---|---|
| 데이터 위치 | React state | DOM |
| 실시간 검증 | 쉬움 | 어려움 |
| 리렌더링 횟수 | 키 입력마다 | 제출 시점만 |
| 코드 양 | 상대적으로 많음 | 상대적으로 적음 |
| 파일 입력 | 불가 | 가능 |
실시간 피드백(입력 검증, 글자 수 카운트, 조건부 버튼 활성화)이 필요하거나, 다른 컴포넌트와 상태를 공유해야 한다면 Controlled Component가 적합합니다.
반면, 제출 시점에만 값이 필요하고 중간 상태 추적이 불필요한 단순한 폼이라면 Uncontrolled Component가 코드를 더 단순하게 만들어 줍니다. 파일 업로드는 항상 Uncontrolled 방식으로 처리해야 합니다.
대부분의 React 애플리케이션에서는 Controlled Component가 권장되는 기본값입니다. React의 선언형 데이터 흐름을 활용하면 상태와 UI 간의 불일치로 인한 버그를 방지할 수 있기 때문입니다. 단, 성능이 중요한 대규모 폼이나 라이브러리(React Hook Form 등)를 도입할 경우에는 Uncontrolled 방식을 기반으로 구현된 경우가 많으므로 두 개념을 모두 이해하고 있어야 합니다.