비제어 컴포넌트(Uncontrolled Component)와 제어 컴포넌트(Controlled Component)

제어 컴포넌트 (Controlled Component)

React의 state가 입력 값을 관리하는 방식으로 useStateuseReducer 등의 state를 사용해 값이 변경될 때마다 UI를 업데이트한다.

특징

  • 입력값이 state에 저장되며, onChange를 통해 값이 업데이트됨
  • React가 폼 입력의 현재 값을 완전히 제어함
  • UI의 값이 state를 통해 단방향으로 흐름
  • 폼 데이터를 쉽게 검증하고 관리 가능 (ex: onChange에서 유효성 검사 적용 가능)

예제

import { useState } from "react";
 
const ControlledInput = () => {
  const [text, setText] = useState("");
 
  return (
    <input
      type="text"
      value={text}
      onChange={(e) => setText(e.target.value)} // React 상태 업데이트
    />
  );
};
 

장점

  • 입력 값이 state에 저장되므로, 유효성 검사와 로직을 쉽게 추가 가능
  • 입력 값 동기화가 React 상태와 일치하여 유지보수가 쉬움
  • Formik, react-hook-form 같은 라이브러리와 쉽게 통합 가능

단점

  • 입력이 많아질수록 state 업데이트가 많아져 성능 저하 가능성
  • 단순한 폼에서도 state와 onChange 처리가 필요하여 코드가 길어질 수 있음

비제어 컴포넌트 (Uncontrolled Component)

React의 state가 아닌, DOM 자체가 값을 관리하는 방식이다. React에서 제공하는 ref를 사용해 필요할 때 DOM에서 값을 직접 가져온다. ref를 사용하는 이유는 React에서 사용하는 ref의 경우 리렌더링을 유발하지 않기 때문에 렌더링 과정에서의 성능적 이점을 누릴 수 있다.

특징

  • useRef를 사용하여 DOM 요소를 직접 조작함
  • valueonChange 없이 <input> 자체의 내부 상태를 활용
  • 필요할 때만 ref.current.value를 통해 값을 읽음
  • 브라우저의 기본 폼 동작을 활용 가능

예제

import { useRef } from "react";
 
const UncontrolledInput = () => {
  const inputRef = useRef<HTMLInputElement>(null);
 
  const handleSubmit = () => {
    if (inputRef.current) {
      alert(`입력 값: ${inputRef.current.value}`);
    }
  };
 
  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
};
 

장점

  • 입력값을 실시간으로 반영할 필요가 없을 때 성능이 좋음
  • 간단한 폼에서 불필요한 state 업데이트를 줄일 수 있음
  • React 외부 라이브러리(jQuery, D3 등)와 통합이 쉬움

단점

  • React가 입력값을 관리하지 않으므로 유효성 검사나 동적 업데이트가 어렵다
  • useRef로 값에 접근해야 하므로 라이브러리를 사용하지 않는다면 코드가 더 드러워질 수 있다