React

[React/리액트] Controlled와 Uncontrolled 컴포넌트: 리액트에서 폼 입력 관리하기

solfa 2024. 6. 29. 21:45

 

리액트(React)에서 폼을 다루다 보면, "Controlled"와 "Uncontrolled" 컴포넌트라는 용어를 접할 수 있다.

제어 컴포넌트 / 비제어 컴포넌트라고 다들 알고있을 것이다!

이 두 가지 패턴은 입력 요소의 상태를 관리하는 방법에서 큰 차이를 보인다.

React-Hook-Form 같은 걸 쓸 때 아무렇게나 쓰지 말고 잘 쓰기 위해서 Controlled와 Uncontrolled 컴포넌트의 차이점, 장단점, 그리고 사용 예시를 살펴보자.

 


Controlled 컴포넌트

Controlled 컴포넌트는 상태가 컴포넌트의 state에 의해 제어되는 입력 요소이다. 이러한 패턴에서는 입력 요소의 값(value)이 컴포넌트의 state에 저장되고, 입력 요소가 변경될 때마다 state가 업데이트된다.

예시 코드

import React, { useState } from 'react';

function ControlledInput() {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (e) => {
    setInputValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleChange} />
      <p>입력값: {inputValue}</p>
    </div>
  );
}

export default ControlledInput;


위 코드에서 `input` 요소의 값은 `inputValue` state에 의해 제어된다. 사용자가 입력할 때마다 `handleChange` 함수가 호출되어 state가 업데이트되고, 이에 따라 `input` 요소의 값도 갱신된다.

Controlled 컴포넌트의 장점
1. 완전한 제어: 폼의 모든 상태가 컴포넌트의 state에 저장되므로, 입력값을 완벽하게 제어할 수 있다.
2. 유효성 검사: 입력값이 변경될 때마다 유효성 검사를 쉽게 수행할 수 있다.
3. 동적 UI 업데이트: 입력값에 따라 동적으로 UI를 업데이트할 수 있다.

Controlled 컴포넌트의 단점
1. 추가 코드: 상태 관리와 이벤트 핸들링을 위한 추가 코드가 필요하다.
2. 복잡성 증가: 폼이 복잡해질수록 코드가 더 복잡해질 수 있다.

 

제어하는 방식에 따라 나뉘는 제어 컴포넌트

제어 컴포넌트가 어디에서 값을 제어하는 지에 따라 나뉠 수도 있다. 예를 들어 외부에서 값을 제어한다거나 내부 상태로 값을 제어한다거나! 예시로 바로 살펴보자.

 

function InputA({
  defaultValue,
  onChange,
}: {
  defaultValue: string;
  onChange: (v: string) => void;
}) {
  const [state, setState] = useState(defaultValue);
  
  return (
    <input
      value={state}
      onChange={(e) => {
        setState(e.target.value);
        onChange(e.target.value);
      }}
    />
  );
}

 

 

이 컴포넌트는 defaultValue prop을 사용하여 초기 값을 설정하고 내부 상태 state를 사용하여 값을 관리한다. 내부 상태로 값을 관리하는 방식이다.

 

function InputB({
  value,
  onChange,
}: {
  value: string;
  onChange: (v: string) => void;
}) {
  return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}

 

이 컴포넌트는 value와 onChange prop을 받아서 input 요소의 값과 변경 이벤트를 제어한다. 외부에서 값을 제어하는 방식이다.


Uncontrolled 컴포넌트

Uncontrolled 컴포넌트는 상태를 자체적으로 관리하지 않는 입력 요소이다. 이러한 패턴에서는 입력 요소의 값이 DOM 노드에 의해 직접 관리된다. 초기 값은 `defaultValue` 속성을 통해 설정할 수 있다.

예시 코드

import React, { useRef } from 'react';

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`입력값: ${inputRef.current.value}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" defaultValue="기본값" ref={inputRef} />
      <button type="submit">제출</button>
    </form>
  );
}

export default UncontrolledInput;


위 코드에서 `input` 요소의 값은 리액트의 state가 아닌 DOM 노드에 의해 직접 관리된다. `useRef` 훅을 사용하여 입력 요소에 접근하고, `handleSubmit` 함수에서 입력값을 가져온다.

Uncontrolled 컴포넌트의 장점
1. 간단한 코드: 상태 관리를 위한 추가 코드가 필요하지 않다.
2. 간편한 초기화: `defaultValue` 속성을 통해 간편하게 초기 값을 설정할 수 있다.

Uncontrolled 컴포넌트의 단점
1. 제어의 어려움: 입력값을 실시간으로 제어하거나 유효성 검사를 수행하기 어렵다.
2. 동적 업데이트 제한: 입력값에 따라 UI를 동적으로 업데이트하는 것이 어렵다.


언제 사용해야 할까?

- Controlled 컴포넌트는 입력값에 대한 완전한 제어가 필요할 때 사용한다.

예를 들어, 유효성 검사, 조건부 렌더링, 입력값 동기화 등이 필요한 경우에 적합하다.

state의 특성 상 변경되면 리렌더링이 발생하기 때문에 입력 때마다 즉각적인 유효성 검사를 하는 경우나 값이 입력됐을 때에만 submit 버튼을 활성화하는 등 실시간적인 처리가 들어가야 할 때 유용하다.

 

더 많은 제어와 유연성을 제공하지만, 코드가 복잡해질 수 있다. 


- Uncontrolled 컴포넌트는 간단한 폼이나 초기 값 설정만 필요한 경우에 사용한다.

예를 들어, 사용자가 제출한 폼 데이터를 처리하는 간단한 시나리오에 적합하다.

제어 컴포넌트와는 다르게 입력이 발생할 때마다 리렌더링을 일으키지 않아 성능 상의 이점이 있다.
더 간단한 구현(핸들러 작성 필요가 없음)을 가능하게 하지만, 제어가 어렵다. 

 

결론

 

실시간으로 값이 필요할 때는 제어 컴포넌트(Controlled Component)를 사용하고

불필요한 재렌더링을 줄이고 제출 시에만 값이 필요할 때는 비제어 컴포넌트(UnControlled Component)를 사용하는 것이 좋다.

728x90