입력창에 타이핑이 안 되고 controlled 경고까지 뜬 이유
문제 발생
서버에서 받은 값으로 폼을 채웠더니 두 가지 문제가 한 번에 났습니다. 처음에는 아예 타이핑이 되지 않았고, onChange를 붙인 뒤에는 콘솔 경고가 떴습니다.
const [user, setUser] = useState(null); // 로딩 중에는 null
<input value={user?.name} onChange={...} />Warning: A component is changing an uncontrolled input to be controlled.원인 분석
두 개의 다른 문제입니다.
첫 번째는 value만 준 경우입니다. React 문서가 경고 문구를 그대로 보여줍니다 — 폼 필드에 onChange 핸들러 없이 value prop을 주었습니다. 이는 읽기 전용 필드를 렌더합니다. 필드가 변경 가능해야 한다면 defaultValue를 쓰고, 그렇지 않다면 onChange나 readOnly를 설정하세요. React가 값을 state에 고정하므로 키 입력이 화면에 반영되지 않습니다.
두 번째가 이번 경고의 원인입니다. 문서는 규칙을 이렇게 정리합니다 — 컴포넌트에 value를 준다면 그 수명 동안 계속 문자열이어야 합니다. 처음에 value={undefined}를 주고 나중에 value="some string"을 줄 수는 없습니다. React가 이 컴포넌트를 비제어로 원했는지 제어로 원했는지 알 수 없기 때문입니다.
user가 null인 첫 렌더에서 user?.name은 undefined입니다. React는 이를 비제어 입력으로 취급하고, 데이터가 도착해 문자열이 들어오는 순간 제어 입력으로 바뀝니다. 이 전환에서 사용자가 이미 입력하던 값이 덮어써지는 실제 버그도 함께 생깁니다.
해결 방안
value가 항상 문자열이 되게 합니다. 문서가 권하는 방법 그대로입니다.
<input value={user?.name ?? ""} onChange={(e) => setName(e.target.value)} />- 초기값만 지정하고 싶으면 비제어로 갑니다.
<input defaultValue={user?.name} />-
읽기 전용이 의도라면 그렇게 선언합니다.
readOnly를 붙이면 경고도 사라지고 의도도 드러납니다. -
데이터 도착 시점 문제를
key로 정리합니다. 로딩이 끝난 뒤 폼을 새로 시작해야 한다면, 조건부로 값을 바꾸는 대신<Form key={user.id} …/>로 새로 마운트하는 편이 예측 가능합니다. -
체크박스도 같은 규칙입니다. 문서가 함께 적듯
checked를 넘긴다면 항상 boolean이어야 합니다.undefined가 섞이면 동일한 전환이 일어납니다. -
컴포넌트를 만들 때 한쪽을 정합니다. 문서의 핵심 원칙입니다 — 생성 시점에 제어인지 비제어인지 정하고 수명 내내 그 결정을 유지합니다. 재사용 컴포넌트라면 이 계약을 props 타입으로도 드러냅니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.