[React] Controlled vs Uncontrolled 컴포넌트

Sara Jo·2025년 5월 18일
post-thumbnail

TL;DR: 내부 상태(state)로 스스로 동작하면 비제어(Uncontrolled), 부모가 props로 모든 핵심 정보를 내려주면 제어(Controlled) 컴포넌트


1. 개념 정의

React 공식 문서에서는 다음과 같이 설명하고있다.

It is common to call a component with some local state “uncontrolled”. For example, the original Panel component with an isActive state variable is uncontrolled because its parent cannot influence whether the panel is active or not.

In contrast, you might say a component is “controlled” when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final Panel component with the isActive prop is controlled by the Accordion component.

Uncontrolled components are easier to use within their parents because they require less configuration. But they’re less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.

In practice, “controlled” and “uncontrolled” aren’t strict technical terms—each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.

When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.

(의역):

지역 상태(state)를 가진 컴포넌트는 일반적으로 비제어(uncontrolled) 컴포넌트라 부른다. 예를 들어 isActive 라는 상태를 내부에 두고 있는 초기 Panel 컴포넌트는 부모가 활성/비활성 여부를 조정할 수 없으므로 비제어다.

반대로, 중요한 정보가 로컬 state가 아니라 props로 전달될 때 이를 제어(controlled) 컴포넌트라 한다. 이렇게 하면 부모가 컴포넌트의 동작을 완전히 정의할 수 있다. isActive prop을 받는 최종 Panel 컴포넌트가 Accordion 컴포넌트에 의해 제어되는 것이 그 예다.

비제어 컴포넌트는 설정할 것이 적어 쓰기 간편하지만, 여러 컴포넌트를 함께 조정하기엔 유연성이 낮다. 제어 컴포넌트는 최대한의 유연성을 제공하지만, 부모가 세세히 관리해야 한다는 부담이 있다.

실제로 대부분의 컴포넌트는 state와 props를 적절히 섞어 쓴다. 어떤 정보를 제어할지/비제어로 둘지 고민하고, 필요하다면 언제든 리팩터링하자.


2. 특징 비교

구분비제어(Uncontrolled)제어(Controlled)
데이터 소스컴포넌트 내부 useState부모 props
사용 난이도간단 (빠른 프로토타입)복잡 (설정 필요)
재사용·조율제한적 (상태 분리 어려움)매우 유연 (부모에서 완전 제어)
대표 사례Form 입력을 ref로 직접 제어Form 입력을 value/onChange로 제어
장점설정 적음, 코드 짧음상태 단일 source of truth 유지, 테스트 용이
단점상태 분산, 예측 어려움보일러플레이트 증가

📝  :
작은 위젯·독립 모듈 → 비제어
대규모 폼·여러 컴포넌트가 동일 상태를 공유 → 제어 패턴이 적합


3. 코드 예시

3‑1. 비제어 Panel

// 부모가 알 필요 없는 간단 토글 패널
function Panel({ title, children }) {
  const [isActive, setIsActive] = React.useState(false);

  return (
    <section>
      <h3 onClick={() => setIsActive(!isActive)}>{title}</h3>
      {isActive && <div>{children}</div>}
    </section>
  );
}
  • 장점 : panel 하나만 쓰면 끝. 설정 없음.
  • 단점 : 여러 패널을 한꺼번에 열거나 닫는 등, 부모가 상태를 조정하기 어렵다.

3‑2. 제어 Panel + Accordion

function Panel({ title, isActive, onToggle, children }) {
  return (
    <section>
      <h3 onClick={onToggle}>{title}</h3>
      {isActive && <div>{children}</div>}
    </section>
  );
}

function Accordion({ items }) {
  const [activeIndex, setActiveIndex] = React.useState(null);

  return (
    <div>
      {items.map((item, idx) => (
        <Panel
          key={idx}
          title={item.title}
          isActive={activeIndex === idx}
          onToggle={() =>
            setActiveIndex(activeIndex === idx ? null : idx)
          }
        >
          {item.content}
        </Panel>
      ))}
    </div>
  );
}
  • 장점 : Accordion 이 모든 Panel 의 열림/닫힘을 통제. 복잡한 UX도 쉽게 구현.
  • 단점 : Panel 사용 시 반드시 isActive 와 onToggle 을 내려줘야 한다.

3‑3. 입력 필드 비교

// 비제어 input (ref 이용)
function UncontrolledInput() {
  const inputRef = React.useRef();

  function handleSubmit(e) {
    e.preventDefault();
    alert(inputRef.current.value);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" ref={inputRef} />
      <button>Submit</button>
    </form>
  );
}

// 제어 input (state 이용)
function ControlledInput() {
  const [value, setValue] = React.useState("");

  function handleChange(e) {
    setValue(e.target.value);
  }

  return (
    <input type="text" value={value} onChange={handleChange} />
  );
}
  • 비제어 : DOM value 를 직접 읽으므로 구현이 빠르지만, 값 변화를 실시간으로 추적하기 어렵다.
  • 제어 : React state가 단일 source of truth. 실시간 검증·포맷팅·공유가 쉬움.

4. 언제 어떤 방식을 써야 할까?

상황추천 방식
독립적인 위젯 · 빠른 프로토타입비제어
많은 입력 필드가 서로 의존 · 실시간 검증제어
부모에서 다양한 조합/조건부 렌더링 필요제어
DOM API(파일 업로드, 비디오 등)와 직접 상호작용비제어 또는 hybrid

✅ 결론 : 고민되면 우선 비제어로 시작 → 복잡해지면 제어로 리팩터링!


5. 요약

  • 비제어 컴포넌트는 구현이 간단하지만 상태 공유가 어렵다.
  • 제어 컴포넌트는 유연성이 극대화되지만, 부모에서 모든 상태를 책임져야 한다.
  • 실제 서비스에서는 두 방식을 혼합하여 상황에 맞는 최적의 DX(Developer Experience)를 찾아보자.

0개의 댓글