폼
평범한 HTML input에 입력하면 브라우저가 값을 관리합니다. 입력한 내용을 저장하고 커서를 다시 그리는데, 여기에 코드가 필요 없습니다. 이 input의 value를 state에 연결하고 onChange에서 그 state를 업데이트하면, input이 React의 다른 모든 것처럼 동작하기 시작합니다. state가 페이지에 표시되는 내용의 원천이 되고, input이 이를 반영합니다. 이것이 제어된 입력값이며, React가 매 순간 무엇을 표시할지 결정합니다.
여기 텍스트 입력 하나가 있는 간단한 폼이 있습니다:
function NameForm() {
const [text, setText] = useState('')
function handleSubmit(event) {
event.preventDefault()
console.log(text)
}
return (
<form onSubmit={handleSubmit}>
<input value={text} onChange={e => setText(e.target.value)} />
<button>Submit</button>
</form>
)
}input의 value는 text state에서 옵니다. 매번 키를 누를 때마다 onChange가 발생하고, 새로운 값으로 setText를 호출합니다. state와 input은 계속 동기화됩니다. React가 현재 text 값으로 input을 다시 렌더링하기 때문입니다. 기본적으로 폼을 제출하면 페이지가 다시 로드되므로, handleSubmit은 먼저 event.preventDefault()를 호출한 후 앱이 필요한 작업을 합니다. 여기서는 텍스트를 기록하거나 다른 곳의 서버로 보냅니다.
제어된 입력값과 제어되지 않은 입력값
위의 input은 제어된 입력값입니다. 그 값이 React state에 있고 React가 무엇을 표시할지 결정하기 때문입니다. onChange 없이 value를 주면, input이 읽기 전용이 됩니다. React가 매 렌더링마다 같은 값을 계속 칠하기 때문에, 입력한 어떤 것도 state에 도달하지 않습니다. 따라서 필드가 제어될 때는 항상 둘을 함께 사용하세요. input은 또한 제어되지 않을 수 있습니다. DOM이 자신의 값을 추적하고, 필요할 때만 값을 읽어옵니다. 단일 필드의 경우 ref(Refs and the DOM에서 자세히 다룸)를 통해, 또는 전체 폼의 경우 제출 시간에 폼의 FormData를 통해 읽습니다. 제어된 입력값은 매번 키 입력할 때마다 현재 값을 컴포넌트에 제공합니다. 실시간 유효성 검사, 문자 수 세기, 또는 UI의 다른 부분이 의존하는 필드에 필요합니다. 제어되지 않은 입력값은 매번 키 입력에서 렌더링을 건너뛰고, 제출 시간에만 값을 읽습니다. 단순한 필드에서 다른 것이 감시할 필요가 없을 때는 코드가 더 적습니다.
체크박스
체크박스는 같은 패턴을 따르지만, state가 value 대신 checked에 있습니다:
function Newsletter() {
const [subscribed, setSubscribed] = useState(false)
return (
<label>
<input
type="checkbox"
checked={subscribed}
onChange={e => setSubscribed(e.target.checked)}
/>
업데이트 구독
</label>
)
}e.target.checked는 boolean이고, subscribed가 체크박스에 체크 표시가 되어 있는지를 결정합니다. 한 개의 prop, 한 개의 변경 핸들러, 텍스트 입력값과 같은 형태입니다.
선택 드롭다운
<select>도 value를 받지만, 선택된 option이 아닌 select 자체에 설정됩니다:
function ColorPicker() {
const [color, setColor] = useState('red')
return (
<select value={color} onChange={e => setColor(e.target.value)}>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
)
}select에 value를 설정하면, React가 일치하는 option을 선택해줍니다. 어떤 option 요소에도 selected 속성을 추가할 필요가 없습니다.
Textarea
HTML에서 textarea는 여는 태그와 닫는 태그 사이에 텍스트를 담습니다. React는 다른 입력값처럼 취급하므로, 텍스트는 value prop을 통해 도착하고 onChange가 동기화를 유지합니다:
function Feedback() {
const [message, setMessage] = useState('')
return (
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
rows={4}
/>
)
}rows는 상자가 시작할 높이를 설정합니다. defaultValue는 여기서 value의 제어되지 않은 동등물입니다. 시작 텍스트를 한 번 설정하고 DOM에서 추적하도록 둡니다.
라디오 버튼
여러 입력값이 하나의 name을 공유하는데, 이것이 브라우저에 이들이 함께 속해 있다고 알립니다. 하나만 선택될 수 있습니다. React에서 그룹은 하나의 state를 공유하고, 각 입력값의 checked는 이에 대해 비교합니다:
function ShippingSpeed() {
const [speed, setSpeed] = useState('standard')
return (
<fieldset>
<legend>배송 속도</legend>
{['standard', 'express', 'overnight'].map(option => (
<label key={option}>
<input
type="radio"
name="speed"
value={option}
checked={speed === option}
onChange={e => setSpeed(e.target.value)}
/>
{option}
</label>
))}
</fieldset>
)
}fieldset과 legend는 스크린 리더를 위해 그룹을 레이블합니다. 각 label은 옵션이 배열에서 오기 때문에 key를 받습니다 (리스트와 key).
checked={speed === option}은 정확히 한 옵션에 대해 참이므로, 하나의 state 값이 전체 그룹을 포함합니다. 반면 체크박스는 자신의 boolean을 가집니다.
액션으로 제출하기
<form>은 action 함수를 가질 수도 있습니다. React는 폼이 제출될 때 이를 호출하고 폼의 값을 담은 FormData 객체를 전달합니다:
function Signup() {
function handleSignup(formData) {
const values = Object.fromEntries(formData)
console.log(values.email, values.password)
}
return (
<form action={handleSignup}>
<input name="email" type="email" />
<input name="password" type="password" />
<button>가입</button>
</form>
)
}여기서는 event가 없으므로, preventDefault()를 호출할 것도 없습니다. React가 브라우저의 기본 페이지 재로드를 멈춰줍니다.
Object.fromEntries(formData)는 그 FormData를 한 단계로 평범한 객체로 변환합니다. 키는 각 input의 name 속성에서 옵니다. 따라서 폼의 모든 input에 이름이 필요합니다. 여러 input이 이름을 공유하고 함께 전송될 수 있을 때, 예를 들어 체크박스 그룹일 때, 마지막 값만 남으며 formData.getAll이 모두를 읽습니다.
위의 입력값들은 제어되지 않습니다. DOM이 제출할 때까지 각 값을 보관합니다. 이것은 onSubmit에서 사람들을 놀라게 하는 한 가지 동작에 중요합니다. React는 액션이 해결되면 폼을 재설정하고, 이렇게 제어되지 않은 필드들을 비웁니다. 제어된 필드는 state에서 다시 렌더링되어 그 값을 유지합니다.
각 경로는 다양한 종류의 폼에 맞습니다. 액션은 값을 모아서 건네주는 것이 직업인 폼, 보통 서버에, 대기가 실제이고 결과가 UI로 돌아와야 할 때입니다. 값들이 모여서 이름이 붙어옵니다. 제어된 입력값과 함께 onSubmit은 사용자가 입력하는 동안 반응해야 하는 폼에 맞습니다. 실시간 유효성 검사, 문자 수, 또는 필드가 폼의 나머지를 변경하도록 합니다.
버전 참고
폼 액션은 React 19에서 도입되었습니다. React 18 이하에서는 <form action={handleSignup}>이 에러를 던지지 않습니다. 함수는 유효한 속성 값이 아니기 때문에 React가 속성을 버리고 개발 환경에서 경고합니다. 그러면 폼이 현재 URL로 제출되어 페이지가 재로드되고 함수는 실행되지 않습니다. 이 버전들에서 제출은 event.preventDefault()와 함께 onSubmit을 통합니다.
value는 state에서 오고, onChange는 그 state를 업데이트합니다. 이것이 텍스트 입력값에 대해 클릭되면, 선택 드롭다운과 textarea에 같은 방식으로 작동하고, checked를 가진 체크박스와 라디오 버튼에도 state를 제공합니다. 제출은 자신의 단계입니다. 폼의 제출은 작성한 함수를 실행하고, 그 함수가 값으로 무엇을 할지 결정합니다. 다음: State 끌어올리기. 두 컴포넌트가 같은 state를 공유해야 할 때입니다.

