제어 컴포넌트와 비제이 컴포넌트의 차이에 대해 설명해주세요

mimmi·2024년 12월 27일
0

React

목록 보기
7/9
post-thumbnail

React 개발을 하다보면 form과 input 등 입력 요소 태그를 사용할 일이 빈번히 일어납니다. 현재 진행하고 있는 프로젝트에서도 매우 많은 form을 사용하고 있습니다.

해당 요소에 접근해 값을 변경하면 state 변경에 의해 리렌더링이 일어나는 경우를 자주 확인할 수 있는데 이런 form 요소 컴포넌트에 대해 밀접한 관련이 있는 제어 컴포넌트, 비제어 컴포넌트를 알아보려 합니다.

Form Tag Elements

HTML에서 주로 사용되는 Form 관련 tag들은 inputtextareaselect등이 있습니다. 사용자가 특정 값을 입력하거나 선택해서 변경할 수 있는 태그들입니다.

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를 가져오기에는 번거롭고, 변수에 할당해서 사용하기에는 위에서 언급한 신뢰 가능한 단일 출처의 문제점이 존재했기에 제어 컴포넌트를 활용해서 문제점을 해결했습니다.

제어 컴포넌트(Controlled Component)

(공식문서에 의하면…)

  • HTML에서 inputtextareaselect와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다.
    React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState에 의해 업데이트됩니다.
  • React state를 "신뢰 가능한 단일 출처"로 만들어 두 요소를 결합할 수 있습니다.
    그러면 폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어합니다.
  • 이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 `제어 컴포넌트(controlled component)라고 합니다.
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를 해주고
    그 name을 value attribute에 할당을 해주고 있습니다.
  • value attribute와 state를 결합해서 name state를 신뢰 가능한 단일 출처로 만들어주는겁니다.
    이렇게 form에 사용자 입력값을 React가 제어하기 때문에 위 코드는 제어 컴포넌트의 대표적인 예시 코드가 될 수 있습니다.

비제어 컴포넌트(Uncontrolled Component)

  • 비제어 컴포넌트는 제어 컴포넌트와 달리 state에 의해 값이 관리 및 변경되지 않는 컴포넌트입니다. React가 아닌 기존 자바스크립트에서 사용하던 방식이랑 동일합니다.
  • 자바스크립트에서 폼을 제출할때 submit 버튼을 사용해서 태그 요소 내부의 값을 가져왔습니다. 비제어 컴포넌트 또한 이와 유사한 방식으로 사용됩니다.
  • 제어 컴포넌트에서 사용했던 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>)
}
  • 위 코드는 비제어 컴포넌트의 대표적인 예시 코드입니다.
  • 코드를 보면 알다시피 state가 아닌 ref를 통해 input의 값을 가져옵니다. input ref attribute에 name ref를 직접적으로 할당해주고 있습니다.
    ref 같은 경우 자바스크립트 객체이기에 current 프로퍼티를 통해 input 태그에 접근할 수 있고 ref는 변화가 이루어져도 리렌더링이 발생하지 않는 특징을 가지고 있습니다.
  • 해당 코드를 보면 React가 Form에 관여하는 부분이 전혀 없다는걸 알 수 있습니다. 리액트가 form에 입력값을 제어하지 않기 때문에 비제어 컴포넌트가 됩니다. 과거에 많이 사용했던 HTML과 JavaScript를 활용한 form 방식입니다.
    • 하지만 위에서 언급한 신뢰 가능한 단일 출처를 가지는 컴포넌트는 맞습니다. ref를 통해 input attribute에 접근해 값을 가져오기 때문입니다.

비제어 컴포넌트의 단점

  • 값이 실시간으로 동기화되지 않습니다.
    • React는 state 변화가 일어나면 리렌더링을 발생시키고 값을 동기화시켜주는데 해당 작업이 이루어지지 않기 때문입니다.
    • 만약 A와 B 두 개의 컴포넌트가 있을 때 A에 대한 변화를 B가 실시간으로 동기화시켜서 사용자에게 보여줘야할 경우 비제어 컴포넌트는 이런 방식에 대응하지 않기 때문에 의도치 않은 결과값을 사용자에게 제공할 수 있습니다.

제어 컴포넌트 비제어 컴포넌트 차이점

제어 컴포넌트비제어 컴포넌트
데이터 관리 주체ReactDOM
데이터 갱신 시점사용자가 값을 입력할 때마다 갱신특정 시점에 DOM을 통해 갱신
리렌더링 여부값을 입력할 때마다 발생값을 입력할 때는 발생하지 않음
  • 제어 컴포넌트는 항상 최신값을 유지하며 새로운 입력 값이 생길때마다 값이 새로 갱신되고 동기화됩니다.
  • 비제어 컴포넌트는 새로운 입력 값이 생겨도 값이 갱신되지 않으며 submit등 특정 트리거 이벤트를 통해 갱신을 해줘야 합니다.
제어 컴포넌트비제어 컴포넌트
Submit과 같은 일회성 정보 검색OO
Submit시 유효성 검사OO
조건에 따른 Submit 버튼 (비)활성화OX
즉각적인 필드 유효성 검사OX
특정 입력 형식 적용OX
동적 입력OX
  • 간단하게 생각하면 사용자와의 상호작용에 있어서 실시간으로 변화가 이루어지는 부분들이 제어 컴포넌트는 가능하지만 비제어 컴포넌트는 불가능합니다.
  • 이렇게만 보면 제어 컴포넌트만 사용하면 되는거 아닌가?라고 생각하실 수 있겠지만 제어 컴포넌트 또한 단점이 존재합니다.

제어 컴포넌트 단점

  1. 사용자 입력 값이 변경될 때마다 리렌더링 발생
  2. 모든 Form Elements에 React 상태가 연결되어 있음
  3. React를 사용하지 않은 Form 코드와 통합이 어려움

  • input에서 글자를 입력하고 지울 때마다 리렌더링이 발생한다는건 불필요한 메모리가 사용된다는 부분과 동일합니다.
    • 그렇기에 즉각적으로 반응이 필요한 값이 아니라면 오히려 비제어 컴포넌트처럼 submit할때만 데이터를 변화시켜주는게 더 효율적인 경우도 있다고 생각합니다.
  • 이러한 제어 컴포넌트와 비제어 컴포넌트의 문제점을 최소화하고 렌더링 최적화를 해주는 대표적인 라이브러리도 react-hook-form이 존재합니다.

0개의 댓글