---
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]]