frontend/react

[React] React.js 강좌 8. 폼(Form)과 입력값 제어 (Controlled Components)

mirabo01 2025. 11. 11. 10:56

1. 리액트에서 폼이 중요한 이유

리액트를 사용하다 보면 사용자 입력을 다뤄야 하는 경우가 매우 많습니다.
회원가입, 로그인, 검색창, 댓글 입력 등 — **폼(Form)**은 거의 모든 웹 애플리케이션의 기본입니다.

일반 HTML에서는 <input>의 값을 브라우저가 직접 관리하지만,
리액트에서는 컴포넌트가 입력값을 직접 관리합니다.
이 방식을 **Controlled Component (제어 컴포넌트)**라고 부릅니다.


2. Controlled Component란?

Controlled Component는 말 그대로 **컴포넌트가 입력값을 제어(control)**하는 구조입니다.
즉, 입력 필드의 값이 컴포넌트의 state와 연결되어,
사용자의 입력이 있을 때마다 state를 업데이트합니다.

아래 예제를 보세요.

import React, { useState } from 'react';

function NameForm() {
  const [name, setName] = useState('');

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

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`입력한 이름: ${name}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        이름:
        <input type="text" value={name} onChange={handleChange} />
      </label>
      <button type="submit">제출</button>
    </form>
  );
}

export default NameForm;

이 코드의 핵심은 value={name}과 onChange={handleChange}입니다.
이 두 줄 덕분에 입력창의 값이 항상 state와 일치하게 됩니다.


3. 왜 Controlled Component를 사용하는가?

그 이유는 간단합니다 — 리액트의 단방향 데이터 흐름 때문입니다.

HTML에서는 입력 필드가 스스로 값을 관리하지만,
리액트는 모든 데이터를 컴포넌트 상태(State)로 통합해 관리합니다.
따라서 입력값을 state에 저장하면,

  • 유효성 검사(Validation)를 쉽게 수행할 수 있고
  • 입력값을 다른 컴포넌트나 API 요청에 쉽게 연결할 수 있으며
  • 특정 상황에서 입력을 비활성화하거나 초기화하기도 간단합니다.

4. 여러 개의 입력 필드 관리하기

입력 필드가 여러 개라면 name 속성을 활용하면 효율적으로 관리할 수 있습니다.

function SignupForm() {
  const [form, setForm] = useState({
    username: '',
    email: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setForm({ ...form, [name]: value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(form);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        placeholder="이름"
        value={form.username}
        onChange={handleChange}
      />
      <input
        name="email"
        placeholder="이메일"
        value={form.email}
        onChange={handleChange}
      />
      <button type="submit">등록</button>
    </form>
  );
}

여기서 setForm({ ...form, [name]: value })는
기존 객체를 복사하면서 입력된 필드의 값만 업데이트합니다.
이렇게 하면 여러 입력값을 한 state에서 깔끔하게 관리할 수 있습니다.


5. 체크박스(Checkbox) 제어하기

체크박스도 Controlled Component로 다룹니다.

function CheckboxExample() {
  const [checked, setChecked] = useState(false);

  return (
    <label>
      <input
        type="checkbox"
        checked={checked}
        onChange={(e) => setChecked(e.target.checked)}
      />
      이용약관에 동의합니다.
    </label>
  );
}

여기서 checked 속성은 단순히 value 대신 사용됩니다.
e.target.checked는 boolean 값을 반환하므로, 상태가 true/false로 전환됩니다.


6. 셀렉트 박스(Select) 제어하기

셀렉트 박스도 동일한 원리로 제어합니다.

function SelectExample() {
  const [language, setLanguage] = useState('javascript');

  return (
    <form>
      <label>
        사용 언어:
        <select value={language} onChange={(e) => setLanguage(e.target.value)}>
          <option value="javascript">JavaScript</option>
          <option value="python">Python</option>
          <option value="java">Java</option>
        </select>
      </label>
      <p>선택한 언어: {language}</p>
    </form>
  );
}

select 태그의 value도 리액트가 제어하므로,
사용자가 선택할 때마다 state가 업데이트되고 자동으로 반영됩니다.


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

가끔은 폼 데이터를 굳이 상태로 관리하지 않아도 될 때가 있습니다.
그럴 때는 **Uncontrolled Component (비제어 컴포넌트)**를 사용합니다.
이 방식에서는 ref를 이용해 DOM 요소에 직접 접근합니다.

import React, { useRef } from 'react';

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

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

  return (
    <form onSubmit={handleSubmit}>
      <input ref={inputRef} placeholder="이름 입력" />
      <button type="submit">확인</button>
    </form>
  );
}

비제어 컴포넌트는 간단하지만,
상태 변화에 따른 리렌더링이나 유효성 검사가 어려워
대부분의 경우 Controlled 방식이 더 적합합니다.


8. Controlled vs Uncontrolled 비교

구분 Controlled Component Uncontrolled Component

관리 주체 React state DOM 자체
장점 일관된 데이터 관리, 유효성 검사 용이 코드 간결, 빠른 구현
단점 코드 길어짐, 약간의 오버헤드 상태 추적 불가능, 유지보수 어려움
주로 사용되는 곳 로그인, 회원가입, 입력 검증 단순한 검색창, 임시 입력값

9. 마무리

이번 강의에서는 리액트에서 폼 데이터를 다루는 핵심 방법,
Controlled Component 개념과 다양한 입력 요소 제어 방식을 배웠습니다.

핵심 포인트를 정리하면 다음과 같습니다.

  • 모든 입력값은 value와 onChange를 통해 state와 연결된다.
  • 여러 입력을 관리할 때는 객체 상태와 [name] 문법을 활용한다.
  • 체크박스 → checked, 셀렉트 박스 → value로 제어한다.
  • useRef를 활용한 Uncontrolled 방식도 존재하지만 제한적이다.

다음 글에서는 리액트의 핵심 개념 중 하나인 **Hooks(useState, useEffect)**를 다뤄보겠습니다.
함수형 컴포넌트에서 상태와 생명주기를 관리하는 방법을 단계별로 살펴보겠습니다.