id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id |
title |
category |
status |
source_trust_level |
verification_status |
created_at |
updated_at |
tags |
tech_stack |
applied_in |
aliases |
| react-controlled-vs-uncontrolled |
Controlled vs Uncontrolled 컴포넌트 |
Coding |
draft |
B |
conceptual |
2026-05-09 |
2026-05-09 |
| react |
| forms |
| state |
| vibe-coding |
|
| language |
applicable_to |
| TypeScript / React 18+ |
|
|
|
| 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.
💻 코드 패턴
🤔 의사결정 기준
| 상황 |
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 권유.
🔗 관련 문서