본문 바로가기
개발 공부 일지/React

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

by yelimu 2025. 1. 10.

React Hook Form(리액트 훅 폼)은 비제어 컴포넌트 기반으로 성능 최적화를 위한 라이브러리 이다. 

그런데 비제어 컴포넌트가 왜 좋은지? 제어 컴포넌트는 무엇인지? 제대로 모르고 있어서 정리해보려고 한다.


📖 간략히 개념 정리

제어 컴포넌트 : 부모가 자식의 상태를 관리 

자식은 부모로부터 prop으로 상태를 전달 받음

 

비제어 컴포넌트 : 자신이 자신의 상태를 관리. 즉 부모는 자식의 상태를 알지 못함

자식 컴포넌트의 isActive 속성은 부모로부터 prop로 전달받은 activeIndex 에 의해 결정된다

https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components

 

 

“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.

 

하나의 컴포넌트는 local state와 props 둘다 가질 수 있다. 

따라서 컴포넌트 설계 시 어떤 정보가 props를 통해 전달되어야하며(controlled),

어떤 정보가 로컬 state로 관리되어야 할지(uncontrolled) 고려해야 한다.

하지만 언제든 마음을 바꾸고 리팩토링할 수 있다. (하게된다 아님?ㅋㅋ) 

즉 어느게 좋고 다른게 나쁘고 틀렸다는건 아니라는 말씀


📰근데 이제 역사를 곁들인..

traditional HTML 

리액트의 등장 이전의, 전통적인 HTML 방식에서 폼 요소는 비제어 컴포넌트 방식으로 동작했다.

HTML의 폼 요소라 하면, input, textarea, select, 등이 있는데

사용자가 값을 입력하면 DOM 내부에서 그 값을 저장한다. = 자체적으로 상태를 관리한다

 

JavaScript의 DOM API 를 사용해서 DOM 요소에 직접 접근하여 입력값을 읽고 처리하는 방식

 

▼ DOM API : 브라우저 환경에서 JavaScript를 사용해 HTML 요소에 접근하는 기본 도구 

더보기
getElementById
getElementsByClassName  
getElementsByTagName  
querySelector  
querySelectorAll  
createElement  
appendChild  
removeChild  
setAttribute  
document.title  
element.innerHTML  
element.textContent  
addEventListener  
removeEventListener  
이러한 방식의 한계로는 
  • 여러 입력 필드가 있는 복잡한 폼에서 상태를 관리하는 일이 어렵고, 
  • DOM API를 사용하는 코드가 복잡하며
  • UI와 데이터가 불일치하는 경우가 발생했다. 

React

[제어 컴포넌트]

이러한 문제를 해결하기 위해 리액트에서는 폼 상태를 컴포넌트의 상태 (state)로 관리할 수 있는 제어 컴포넌트가 도입 되었다. 

 

A form element becomes "controlled" if you set its value via a prop. That's all.

prop으로 값을 전달한다면 그것이 controlled 컴포넌트이다

 

[동작 방식]

입력값 변경 → onChange 호출 → 상태 업데이트 → value 재렌더링

 

값이 변경될때 onChange 핸들러가 호출되고 해당 값이 value로 input에 전달된다 

=> date(state)와 UI가 항상 일치하는 값을 갖는다 

=> 즉 입력값에 즉시 반응한다

=> 유효성 검사, 특정 조건 하에 버튼 disabled, 입력값의 포맷을 강제 등 로직 추가하기 쉽다 

 

단점

  • 상태 업데이트로 인해 렌더링 비용이 발생

[비제어 컴포넌트]

리액트에서도 비제어 컴포넌트를 다룰 수 있다. 

이때는 DOM API 를 사용하지 않고 리액트 ref를 사용한다. 

왜? ref를 사용함으로써

  • React 의 생명주기와 통합되어 관리하기가 더 쉬우며
  • React의 선언적 방식을 유지할 수 있다. 
  • 또한 더 간결하고 일관된 방식을 사용할 수 있다. 

자바스크립트로 DOM에 직접 접근하는 것은 권장하지 않는다는 이유가 이거였구나

 

https://goshacmd.com/controlled-vs-uncontrolled-inputs-react/

 

https://goshacmd.com/controlled-vs-uncontrolled-inputs-react/

 

goshacmd.com

 

 


💡 마지막으로

<input type="file" />

파일 인풋은 비제어 컴포넌트로 취급한다.

프로그래밍적으로, 즉 JavaScript로 값을 설정 할 수 없고 사용자만이 값(파일)을 설정할 수 있는 읽기 전용 요소이기 때문에 항상 비제어 컴포넌트이다.

파일을 prop 으로 전달할 수는 있지만 이를 value로 전달할 수는 없다. 

악의적으로 파일 입력 필드가 조작되는것을 보호하기 위해 파일을 선택하는 것은 사용자의 의도에 의해서만 이루어지도록 제한하기 위함이다.