리액트는 <input>
, <textarea>
, <select>
와 같이 입력받을 수 있는 HTML 폼 요소들을 제어 컴포넌트와 비제어 컴포넌트 두 가지의 개념으로 구분하고 있다.
이 글에서는 제어, 비제어 컴포넌트가 무엇인 지 소개하고 상황에 따라 어떻게 사용해야 적절한 지 소개하며 글을 마친다.
리액트에서 사용자의 입력을 받고자 해봤다면 아래와 같은 코드를 보고 다음과 같이 생각할 수 있을 것이다.
<input type='number' value={count} onChange={handleChange}>
“이 input 요소의 값은 count일 것이고, 변경이 발생할 때마다 handleChange가 호출되겠구나.”
만약 value
와 onChange
속성이 없다면 당연히 위와 같은 생각을 할 수 없을 뿐더러 이후 어떠한 값이 될 지 알 수 없다.
또한 사용자가 값을 바꿀 경우 호출되는 이벤트 핸들러 함수 handleChange
에서 count
에 대한 작업을 해줘야만 값이 변경되므로 count
에 대한 작업을 하지 않을 경우에는 입력 폼 요소의 값이 그대로 유지된다는 것을 확신할 수 있다.
이 상황은 마치 입력 폼 요소가 이벤트 핸들러 외에 다른 누군가에 의해 변경될 수 없도록 제어하고 있는 것 같다.
이렇게 입력 폼 요소의 value
속성을 지정하여 값을 제어할 수 있는 컴포넌트를 제어 컴포넌트라고 한다.
위 제어 컴포넌트의 개념과 반대로 생각하면 쉽다. 입력 폼 요소의 value
속성을 제어하지 않는 것이다.
이러한 경우 별도의 value
속성을 지정해주지 않았으므로 화면에는 DOM에 저장된 데이터 값이 나타내진다
자바스크립트에서 document.getElementById('id').value
를 통해 볼 수 있는 그 값이다.
value
속성을 제어하진 않더라도 기본값을 지정해주고 싶은 경우에는 리액트에서 제공하고 있는 defaultValue
, defaultChecked
속성을 통해 해결할 수 있으며 default…
속성은 이후 값이 바뀌더라도 화면에는 반영되지 않는다.
당연한 답변이겠지만 제어 컴포넌트를 사용하도록 해야하며 리액트 공식 문서에서도 다음과 같이 말하고 있다.
“대부분 경우에 폼을 구현하는데 제어 컴포넌트를 사용하는 것이 좋습니다.”
이 말은 단순히 값을 제어하고 사용할 수 있도록 하기 위함을 넘어서 리액트의 핵심 개념이 담겨져 있다.
리액트의 핵심 원칙 중 하나는 단일 진실 공급원이며 모든 데이터 요소를 한 곳에서만 제어하도록 하는 설계이다.
이 원칙은 리액트 공식 문서 중 State 끌어올리기에서 재차 강조되고 있는 원칙이며 읽어볼 것을 추천한다.
리액트 공식 문서에서는 유효성 검사, 방문한 필드 추적 및 폼 제출 처리와 같은 완벽한 해결을 원한다면 Formik이 대중적인 선택 중 하나라고 소개하고 있다.
- https://ko.reactjs.org/docs/forms.html
- https://ko.reactjs.org/docs/uncontrolled-components.html
- https://ko.reactjs.org/docs/lifting-state-up.html
- https://itnext.io/controlled-vs-uncontrolled-components-in-react-5cd13b2075f9
- https://fjorgedigital.com/insights/blog/the-philosophy-of-react-a-single-source-of-truth/