https://reactjs.org/docs/forms.html - 번역 글

HTML 폼 요소는 자체 내부 상태를 갖고 있기 때문에, 폼은 React에서 다른 DOM 요소와 조금 다르게 작동합니다. 예를 들어, 순수한 HTML에서 이 폼은 이름을 받습니다.

<form>
  <label>
    Name:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Submit" />
</form>

이 폼에는 사용자가 폼을 제출할 때 새 페이지로 이동되는 기본 작동을 합니다. 만약 React에서 동일한 동작을 원한다면, 그냥 동작 하도록할 수 합니다. 그러나 대부분의 경우 form 제출을 처리하고 사용자가 form에 입력한 데이터에 접근할 수 있게하는 자바스크립트 함수를 가지는 게 편합니다.
이 동작을 위한 표준 방식은 “제어되는 컴포넌트 (Controlled Components)” 기법을 사용하는 것입니다.


Controlled Components(제어되는 구성 요소)

HTML에서, 폼 등의 요소 <input>, <textarea>, <select>는 일반적으로 자신의 상태를 유지하고 사용자 입력을 기반으로 업데이트합니다. React에서, 변경 가능한 state는 일반적으로 컴포넌트의 state 속성에 존재하며, setState() 로만 업데이트할 수 있습니다.

React state신뢰 가능한 단일 소스 (single source of truth) 로 만들어 두 요소를 결합할 수 있습니다. 그런 다음 렌더링 되는 React 컴포넌트는 이후에 폼에서 발생하는 유저 입력을 제어합니다. 이런 방식으로 React에 의해 제어되는 Input 폼 요소는 “제어되는 컴포넌트” 라고 부릅니다.

예를 들어, 이전 예제가 submit 될 때 이름을 기록하게하려면, 제어 된 구성 요소로 양식을 작성할 수 있습니다.

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

CodePen

value속성은 폼 요소에 설정되므로 표시되는 값은 항상 this.state.value가 되고 React state가 신뢰 가능한 소스가 됩니다. React state를 업데이트하기 위해 모든 키 이벤트에서 handleChange가 동작하기 때문에 사용자가 입력할 때 표시되는 값이 업데이트됩니다.

제어되는 컴포넌트를 사용하면 모든 state변경과 연관되는 핸들러 함수가 생깁니다. 이를 통해 사용자 입력을 수정하거나 검증하는 것이 간단해집니다. 예를 들어 모든 유저의 이름을 강제로 대문자로 받고싶다면 handleChange 를 다음과 같이 쓸 수 있습니다.

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});

The textarea Tag(textarea 태그)

HTML에서, <textarea> 요소는 자식으로 값을 정의합니다.

<textarea>
  Hello there, this is some text in a text area
</textarea>

React에서 <textarea>는 자식값이 아닌value속성을 사용합니다. 이렇게 하면 <textarea> 를 사용하는 폼은 한 줄 입력을 사용하는 폼과 매우 유사하게 작성할 수 있습니다.

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

this.state.valueconstructor에서 설정하기 때문에 textarea는 값을 갖고 시작 할 수 있습니다.


The select Tag(select 태그)

HTML에서, <select> 는 드롭 다운 목록을 만듭니다. 예를 들어, 이 HTML은 과일 드롭 다운 목록을 만듭니다.

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

Coconut옵션에 selected 속성이 있기 때문에 기본적으로 선택되는 걸 주목합시다. React에서는 selected 속성을 사용하는 대신 루트 select 태그에 value 속성을 사용합니다. 한 곳에서 업데이트만 하면 되기 때문에 제어되는 컴포넌트에서 사용하기 더 편합니다. 예를 들어,

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

CodePen

이렇게 한다면 <input type="text">, <textarea>, <select>는 모두 유사하게 작동합니다. 이것들은 제어되는 컴포넌트를 구현할때 value속성을 사용할 수 있습니다.

노트
select태그에서 여러 옵션을 선택하고 싶다면 value속성에 배열을 전달합니다.


The file input Tag(input[type="file"] 태그)

HTML에서는 <input type="file">을 사용하여 하나의 파일을 선택하여 서버에 업로드 하거나 File API 를 통해 JavaScript로 조작 할 수 있습니다 .

<input type="file" />

input[type="file"]의 값은 읽기 전용이기 때문에, React 에서 uncontrolled(제어되지않는) 구성요소 입니다.
문서의 뒷부분에서 제어되지 않는 구성요소와 함께 논의 합니다.


Handling Multiple Inputs(여러 Input 제어하기)

여러개의 제어되는 구성요소를 처리하고싶은 경우 name 속성을 추가한 후, event.target.name을 이용합니다.

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

CodePen

주어진 name에 해당하는 state 키를 업데이트 하기 위해 ES6 Computed property name(계산 된 속성)응 사용할 수 있습니다.

this.setState({
  [name]: value
});

이 코드를 아래 ES5 코드와 같습니다.

var partialState = {};
partialState[name] = value;
this.setState(partialState);

namestate의 속성 키 로써 setState()를 통해 state를 병합되므로 변경된 부분만 적용됩니다.


Controlled Input Null Value(제어되는 Input Null 값)

제어되는 컴포넌트value값으로 prop 값을 지정하면 사용자는 입력을 변경할 수 없습니다.
value는 지정했지만 입력하게 하고 싶다면, value값을 null또는 undefined로 지정하면 됩니다.

아래 코드는 처음에는 수정이 불가능하지만 잠시후 편집이 가능하게 됩니다.

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

Alternatives to Controlled Components(제어되는 구성요소에 대한 대안)

데이터를 변경하는것들 마다 이벤트 핸들러를 작성하고 React 컴포넌트에서 모든 input state를 파이프해야하기 때문에 제어되는 컴포넌트를 사용하는 것은 지루할 수 있습니다.
기존 코드베이스를 React로 변환하거나 React 애플리케이션을 React 라이브러리가 아닌 것들을 통합하고 싶을때 귀찮은 일이 될 수 있습니다. 이런 상황에서는 대체기술은 제어되지않는 구성요소를 사용할 수 있습니다.


Fully-Fledged Solutions(완벽해진 솔루션)

유효성 검사, 방문한 필드 추적 및 form submit 처리와 같은 완벽한 솔루션이 필요하다면 Formik는 많은 사람들이 사용하는것 중 하나입니다. 그러나 이것은 제어되는 컴포넌트와 state관리의 동일한 원칙에 따라 작성되었으므로 이를 배우려는 것을 게을리하지 마십시오.