React 개발을 하다보면 form과 input 등 입력 요소 태그를 사용할 일이 빈번히 일어납니다. 현재 진행하고 있는 프로젝트에서도 매우 많은 form을 사용하고 있습니다.
해당 요소에 접근해 값을 변경하면 state 변경에 의해 리렌더링이 일어나는 경우를 자주 확인할 수 있는데 이런 form 요소 컴포넌트에 대해 밀접한 관련이 있는 제어 컴포넌트, 비제어 컴포넌트를 알아보려 합니다.
HTML에서 주로 사용되는 Form 관련 tag들은 input, textarea, select등이 있습니다. 사용자가 특정 값을 입력하거나 선택해서 변경할 수 있는 태그들입니다.
const form = document.querySelector("form");
const onSubmit = (e) => {
e.preventDefault();
console.log(e.target.elements["input"].value);
console.log(e.target.elements["textarea"].value);
console.log(e.target.elements["select"].value);
};
위 코드를 보면 submit event를 감지해서 각각의 태그에 value attribute를 통해서 값을 가져올 수 있습니다.
여기서 value attribute는 사용자가 입력한 값을 가져올 수 있는데 조금 다르게 해석해보면 사용자가 입력한 값이 value attribute에 저장된다고 볼 수 있습니다.
value attribute는 DOM에 존재하기 때문에 사용자가 입력한 값은 DOM에 저장된다고 볼 수 있습니다.
신뢰 가능한 단일 출처는 간단하게 하나의 상태는 한 곳에만 있어야 함을 의미합니다. input 태그에서 onChange로 state 값을 변경하면 입력하는 값들이 실시간으로 콘솔에 찍히는걸 확인할 수 있습니다.
그렇기에 value attribute는 항상 최신화된 값을 가지고 있기 때문에 해당 값을 가지고 다른 작업을 할 수 있는 다시 말하면 신뢰가 가능한 상태입니다.
또한 input tag의 value attribute는 유일무이한 하나의 값이기에 여기서 value attribute는 신뢰 가능한 단일 출처임을 알 수 있습니다.
신뢰 가능한 단일 출처가 중요한 이유가 무엇인지 확인해보겠습니다.
const form = document.querySelector("form");
let inputValue = "";
let textareaValue = "";
let selectValue = "";
const onSubmit = (e) => {
e.preventDefault();
inputValue = e.target.elements["input"].value;
textareaValue = e.target.elements["textarea"].value;
selectValue = e.target.elements["select"].value;
};
위 코드는 각 태그의 value attribute를 let 변수를 활용해 할당해준 코드입니다.
이렇게 되면 input 태그의 상태는 value attribute와 let 변수 두 개의 출처로 나뉘게 됩니다.
그러면 둘 중 하나의 값이 변경되더라도 자동으로 동기화가 되지 않기 때문에 값의 차이를 가지게 되고 이러면 항상 최신화된 값으로 다른 작업을 수행할 수 없게 됩니다. 신뢰 가능한 단일 출처가 중요하게 여겨지는 이유는 이런 문제점을 방지하기 위해서입니다.
React에서 해당 문제점을 해결하기 위해 도입하게 된 개념이 제어 컴포넌트입니다.
매번 value attribute를 가져오기에는 번거롭고, 변수에 할당해서 사용하기에는 위에서 언급한 신뢰 가능한 단일 출처의 문제점이 존재했기에 제어 컴포넌트를 활용해서 문제점을 해결했습니다.
(공식문서에 의하면…)
input, textarea, select와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다.setState에 의해 업데이트됩니다.import { useState } from "react"
const App = () => {
const [name, setName] = useState("");
return (
<form>
<label>name : </label>
<input
value={name}onChange={(e) => {
setName(e.target.value);
}}placeholder="이름을 입력하세요"/>
</form>)
}
name state를 onChange 이벤트가 발생할 때마다 setState를 해주고value attribute에 할당을 해주고 있습니다.신뢰 가능한 단일 출처로 만들어주는겁니다.비제어 컴포넌트는 제어 컴포넌트와 달리 state에 의해 값이 관리 및 변경되지 않는 컴포넌트입니다. React가 아닌 기존 자바스크립트에서 사용하던 방식이랑 동일합니다.setState 방식으로 값을 가져오는게 아닌 ref를 사용해서 값을 가져옵니다.import { useRef } from "react";
const App = () => {
const nameRef = useRef(null);
const submitName = (e) => {
e.preventDefault();
console.log(nameRef.current.value);
};
return (
<form onSubmit={submitName}>
<label>name : </label>
<input ref={nameRef} placeholder="이름을 입력하세요" />
</form>)
}
비제어 컴포넌트의 대표적인 예시 코드입니다.ref를 통해 input의 값을 가져옵니다. input ref attribute에 name ref를 직접적으로 할당해주고 있습니다.비제어 컴포넌트가 됩니다. 과거에 많이 사용했던 HTML과 JavaScript를 활용한 form 방식입니다.| 제어 컴포넌트 | 비제어 컴포넌트 | |
|---|---|---|
| 데이터 관리 주체 | React | DOM |
| 데이터 갱신 시점 | 사용자가 값을 입력할 때마다 갱신 | 특정 시점에 DOM을 통해 갱신 |
| 리렌더링 여부 | 값을 입력할 때마다 발생 | 값을 입력할 때는 발생하지 않음 |
제어 컴포넌트는 항상 최신값을 유지하며 새로운 입력 값이 생길때마다 값이 새로 갱신되고 동기화됩니다.비제어 컴포넌트는 새로운 입력 값이 생겨도 값이 갱신되지 않으며 submit등 특정 트리거 이벤트를 통해 갱신을 해줘야 합니다.| 제어 컴포넌트 | 비제어 컴포넌트 | |
|---|---|---|
| Submit과 같은 일회성 정보 검색 | O | O |
| Submit시 유효성 검사 | O | O |
| 조건에 따른 Submit 버튼 (비)활성화 | O | X |
| 즉각적인 필드 유효성 검사 | O | X |
| 특정 입력 형식 적용 | O | X |
| 동적 입력 | O | X |