• ABOUT
  • PORTFOLIO
  • POSTS
  • GUESTBOOK

© 2025-2026 BlueCool12 All rights reserved.

2025.08.31React

🎮 Controlled와 Uncontrolled - 리액트 폼 컴포넌트 이해하기

리액트는 폼(Form) 요소의 데이터를 관리하는 방식에 따라 제어 컴포넌트와 비제어 컴포넌트로 구분한다.


1. 제어 컴포넌트 (Controlled Component)

폼 요소의 값을 리액트의 state가 관리하는 방식이다.

state가 단일 데이터 출처가 되며 폼 입력 요소의 value 속성은 state에 의해 결정된다. 사용자의 입력이 발생하면 onChange 이벤트를 통해 state를 업데이트한다.

import { useState } from 'react';

export default function Controlled() {
const [name, setName] = useState('');

return (
<label>
이름
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
);
}

사용자가 input에 값을 입력하면 onChange 이벤트가 발생하고 setName(e.target.value)를 통해 state가 업데이트된다. 이후 state 변경에 따른 컴포넌트 리렌더링이 발생하며 input의 value가 새로운 state 값으로 갱신되는 방식이다.


- 제어 컴포넌트 활용

1) 실시간 유효성 검사

if(name.length < 2) {
// 경고 메시지 표시
}


2) 입력값 기반 UI 변경

{/* 입력값이 없으면 버튼 비활성화 */}
<button disabled={!name}>제출</button>


3) 입력 형식 강제

<input
type="text"
value={name}
onChange={(e) =>
setName(
// 숫자만 입력 허용
e.target.value.replace(/[^0-9]/g, '')
)
}​
/>


2. 비제어 컴포넌트(Uncontrolled Component) 

폼 데이터를 DOM이 직접 관리하는 방식이다.

즉 입력값을 state로 매번 관리하지 않고 필요할 때만 가져오는 방식이다. 리액트에서는 보통 ref를 사용해 DOM 요소에 직접 접근하여 값을 읽는다.

import { useRef } from 'react';

export default function Uncontrolled() {
const inputRef = useRef(null);

function handleSubmit(e) {
e.preventDefault();
const value = inputRef.current?.value ?? '';
alert('입력값: ' + value);
}

return (
<form onSubmit={handleSubmit}>
<label>
이름
<input
type="text"
defaultValue="BlueCool"
ref={inputRef}
/>
</label>
<button type="submit">등록</button>
</form>
);
}

폼 데이터의 출처가 state가 아닌 DOM이 되며 value 대신 defaultValue를 사용한다. 필요할때만 ref를 통해 input 요소에 직접 접근하여 값을 가져온다. 입력 시 state 변경이 없기 때문에 리렌더링이 발생하지 않는다.


- 비제어 컴포넌트 활용

1) 파일 입력
리액트에서는 일반적으로 제어 컴포넌트 사용이 권장되지만 파일 입력의 경우 브라우저 보안 정책 때문에 value를 직접 제어할 수 없다.

import { useRef } from 'react';

function FileInput() {
const ref = useRef(null);

return (
<div>
<input type="file" ref={ref} />
<button
onClick={() => {
if (ref.current) ref.current.value = '';
}}
>
초기화
</button>
</div>
);
}
前の記事
🐞 JPA N+1 문제 - Fetch Join & EntityGraph
次の記事
🤝 읽기 좋은 코드를 위한 네이밍 컨벤션 가이드
装飾ロゴ