--- id: react-controlled-vs-uncontrolled title: Controlled vs Uncontrolled 컴포넌트 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [react, forms, state, vibe-coding] tech_stack: { language: "TypeScript / React 18+", applicable_to: ["Web"] } applied_in: [] aliases: [defaultValue, value prop, ref, form pattern] --- # Controlled vs Uncontrolled 컴포넌트 > "이 input 의 값이 React state 에 사는가, DOM 에 사는가?" 답에 따라 두 패턴. 한 컴포넌트에서 둘 섞으면 사고. **한 가지 결정 후 일관**. ## 📖 핵심 개념 - **Controlled**: `value={state}` + `onChange={setState}`. React 가 단일 진실원. - **Uncontrolled**: `defaultValue` + `ref.current.value`. DOM 이 진실원. 전환은 비싸다. 한 번 controlled 면 영원히 controlled, 그 반대도. React 가 console warning. ## 💻 코드 패턴 ```tsx // ✅ Controlled — 검증/변환/즉시 반영 필요 function NameInput() { const [name, setName] = useState(''); return setName(e.target.value.toUpperCase())} />; } // ✅ Uncontrolled — 큰 form, 제출 시점만 값 필요 function BigForm() { const formRef = useRef(null); const onSubmit = (e: FormEvent) => { e.preventDefault(); const data = new FormData(formRef.current!); sendApi(Object.fromEntries(data)); }; return
; } ``` ## 🤔 의사결정 기준 | 상황 | Controlled | Uncontrolled | |---|---|---| | 입력마다 검증/변환 | ✅ | ❌ | | 다른 UI 가 동시에 반응 | ✅ | ❌ | | 큰 form, 제출만 중요 | ❌ (재렌더 부하) | ✅ | | 파일 업로드 input | ❌ (보안 제한) | ✅ | | 외부 라이브러리 (date picker) | 라이브러리에 따라 | — | | react-hook-form 사용 | hybrid (ref 기반 + controlled API) | — | ## ❌ 안티패턴 - **value 와 defaultValue 모두 지정**: defaultValue 무시 + warning. - **value=undefined 로 시작 → 나중에 string**: controlled 로 전환 → React warning. 항상 ''. - **controlled 인데 onChange 없음**: read-only 처럼 보이는 잠긴 상태 + warning. - **uncontrolled 에서 매번 ref.current.value 읽기 + state 같이 관리**: 둘 동기 안 맞아 사고. - **숫자 input 을 string state**: parseInt/Number 처리 누락 → NaN 폭발. ## 🤖 LLM 활용 힌트 - form 작성 요청 시 "controlled 또는 react-hook-form 둘 중 하나로 일관" 명시. - LLM이 큰 form 을 모두 controlled 로 만들면 성능 risk → react-hook-form 권유. ## 🔗 관련 문서 - [[React_Form_State_Patterns]] - [[React_Refs_Patterns]]