React controlled vs. uncontrolled components

Kyungs·2022년 8월 8일
0

React

목록 보기
8/8
post-thumbnail

리액트를 개발하다보니 프로젝트 특성 상 수 많은 input 태그를 렌더링해야하는 경우가 많다. 서버에서 get 해온 데이터를 렌더링하고, 유저가 input 데이터를 수정한 후 수정 버튼을 누르면 해당 데이터들을 한 데 모아 서버에 put 요청을 보내는 작업이 많았다.

React 공식 문서와 많은 stack overflow의 글들을 참고하여 큰 고민없이 당연하게도(?) controlled 컴포넌트를 사용했다. input 태그에 value={상태}onChange={상태 변경 함수}를 입력하여 유저의 입력값과 상태를 관리했는데, input 태그가 수없이 많아짐에 따라 리렌더링 관련한 이슈가 생겨났다.

controlled component 단점

컴포넌트를 잘 분리해주며 이상적인 컴포넌트를 생성하면 이런 이슈가 없는지도 모르겠다. 다만, 유저가 많은 데이터를 수정했을 때, 해당 데이터들을 한 데 모아 서버에 모두 한 번에 보내기 위해서는 각 value값을 input 태그들의 부모 컴포넌트에서 관리하게 된다. 이 경우, 하나의 input 태그의 데이터를 변경하기만 해도 부모 컴포넌트가 가지고 있는 모든 input 태그가 리렌더링 된다.

input 태그가 몇 개 되지 않는 경우 리렌더링이 오래 걸리지 않기 때문에 큰 문제가 되지 않았다. 하지만 부모 컴포넌트가 갖고 있는 자식 컴포넌트(input태그)가 수십~수백개가 될 경우 리렌더링이 상당히 오래 걸리게 된다. 이 경우 UX가 당연히 좋지 않을 수 밖에 없다.

(map을 사용하여 여러 컴포넌트를 렌더링하는 경우, key 값을 반복되지 않게 설정하는 경우에는 리렌더링 과정에서 focus를 잃게 된다! 물론 이런 실수는 당연히 어떻게든 잡아내야 하지만)

uncontrolled component 고려

uncontrolled component를 사용할 경우 input 태그 안 값의 변경을 리액트 컴포넌트가 아닌 DOM에게 맡기게 된다. 초기 input 태그를 렌더링 하기위해 사용한 React의 상태와 유저가 입력한 값의 동기화는 이뤄지지 않는다. 대신 리액트의 리렌더링을 거치지 않고 유저의 입력 값을 화면에 빠르게 보여줄 수 있게 된다.

나중에 유저가 수정 버튼을 눌러 변경 사항을 서버에 보내야할 경우에 useRef를 이용하여 input 태그의 값들을 불러올 수 있다. 이번 프로젝트의 경우에는 수시로 변하는 렌더링해야하는 input 태그의 갯수에 대응하기 위해 ref 데이터들을 보관할 수 있는 객체를 만들고, 컴포넌트가 호출될 때마다 새로운 ref key-value 셋을 만들어 활용했다.

// useRef 보관 객체 활용 예시
const Child = ({ uniqueComponentName }) => {
  REF_BUNDLE[uniqueComponentName] = React.createRef();
  return (
    <input
      defaultValue={dataFromParent}
      ref={REF_BUNDLE[uniqueComponentName]}
    />
  );
};

const Parent = ({ data }) => {
  const REF_BUNDLE = {};
  return (
    <>
      {data.map((sub, idx) => {
    	return (
          <Child
            uniqueComponentName={name}
    		key={`uniqueKey-${idx}`}
		  />
		);
      })}
    </>
  );
};

정리

리액트 공식문서에서는 거의 모든 상황에서 controlled component를 사용할 것을 권장하지만, 특별한 케이스에서는 (아마 이유가 확실하다면과 비슷한 뜻인것 같다.) uncontrolled component를 사용할 수도 있다고 하고있다.

controlled component를 사용하면 현재 상태와 유저 input이 항상 동기화 되어있기 때문에 서버에 요청을 보낼 때 따로 useRef를 사용해서 값을 불러올 필요가 없다. 또한, 이메일 형식 등을 input 값의 매 변경 때마다 검증하고 싶다면 상태가 매번 동기화되는 controlled component를 사용하는 것이 훨씬 수월하다.

개인적인 의견으로는 부모 컴포넌트에서 관리해야 하는 input 태그가 많고, 리렌더링이 오래걸려 UX가 나빠진다고 판단되는 경우에는 uncontrolled component를 사용하는 것이 더 좋다. 특히, 유저의 매 입력마다 검증이 필요한 로직을 포함하지 않는 경우(ex. onKeyDown, onChange 등의 이벤트를 사용하지 않을 경우)에도 개발자의 판단에 따라 uncontrolled component를 사용할 수 있을 것 같다. 이런 경우에는 input value 값을 필요로 할 때 해당 값을 검증하는 로직이 실행되도록 하면 된다.

참고 자료

[React 공식 문서]Uncontrolled Components
Controlled and uncontrolled form inputs in React don't have to be complicated

0개의 댓글