dev.syw

입력값을 state로 다루는 제어 컴포넌트와 폼 처리.

폼과 제어 컴포넌트

회원가입, 검색, 설정 화면까지 — 웹은 폼으로 가득합니다. React에서는 입력값을 state와 묶어 다루는 제어 컴포넌트 방식이 기본입니다.

학습 목표

  • 제어 컴포넌트가 무엇이고 왜 쓰는지 안다.
  • 여러 입력을 하나의 핸들러로 처리할 수 있다.
  • 체크박스·셀렉트 등 다양한 입력을 다룰 수 있다.
  • 폼 제출을 가로채 원하는 동작을 할 수 있다.
  • 비제어 컴포넌트와의 차이를 안다.

제어 컴포넌트

입력 요소의 value 를 state로 묶고, onChange 로 state를 갱신합니다. 화면에 보이는 값과 state가 항상 일치하게 됩니다.

import { useState } from 'react';

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

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>입력값: {name}</p>
    </div>
  );
}
  • value={name} 으로 입력값을 state가 통제합니다.
  • onChange 가 없으면 입력이 안 바뀌는 읽기 전용처럼 동작하니 짝으로 씁니다.

💡 TIPe.target.value 는 항상 문자열입니다. 숫자가 필요하면 Number(e.target.value) 로 변환하세요.

여러 입력을 한 핸들러로

입력마다 핸들러를 만들면 코드가 길어집니다. name 속성과 객체 state를 활용하면 하나로 끝납니다.

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

  function handleChange(e) {
    const { name, value } = e.target;
    setForm((prev) => ({ ...prev, [name]: value })); // 계산된 키
  }

  return (
    <form>
      <input name="email" value={form.email} onChange={handleChange} />
      <input name="password" value={form.password} onChange={handleChange} />
    </form>
  );
}

[name]: value계산된 프로퍼티 이름으로, name 속성값에 해당하는 필드만 갱신합니다.

체크박스와 셀렉트

입력 종류마다 읽는 속성이 다릅니다.

function Options() {
  const [agree, setAgree] = useState(false);
  const [city, setCity] = useState('seoul');

  return (
    <form>
      <label>
        <input
          type="checkbox"
          checked={agree}                       // value가 아니라 checked
          onChange={(e) => setAgree(e.target.checked)}
        />
        약관 동의
      </label>

      <select value={city} onChange={(e) => setCity(e.target.value)}>
        <option value="seoul">서울</option>
        <option value="busan">부산</option>
      </select>
    </form>
  );
}
입력 종류묶는 속성읽는 값
text / textareavaluee.target.value
checkboxcheckede.target.checked
radiocheckede.target.value
selectvaluee.target.value

폼 제출 처리

<form>onSubmit 에서 preventDefault() 로 새로고침을 막고 원하는 로직을 실행합니다.

function SearchForm() {
  const [query, setQuery] = useState('');

  function handleSubmit(e) {
    e.preventDefault();           // 기본 새로고침 막기
    if (!query.trim()) return;    // 간단한 유효성 검사
    console.log('검색:', query);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <button type="submit">검색</button>
    </form>
  );
}

⚠️ 주의preventDefault() 를 안 부르면 폼이 제출되며 페이지가 새로고침돼 state가 초기화됩니다.

비제어 컴포넌트

state로 묶지 않고, 제출 순간에만 DOM에서 값을 읽는 방식도 있습니다. useRef 로 입력 요소를 참조합니다.

import { useRef } from 'react';

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

  function handleSubmit(e) {
    e.preventDefault();
    console.log(inputRef.current.value); // 제출 시점에만 읽음
  }

  return (
    <form onSubmit={handleSubmit}>
      <input ref={inputRef} defaultValue="" />
      <button>제출</button>
    </form>
  );
}
  • 매 입력마다 리렌더가 없어 간단하지만, 실시간 검증·동기화는 어렵습니다.
  • 파일 입력(<input type="file">)은 보안상 비제어로만 다룰 수 있습니다.
비교제어비제어
값의 출처stateDOM
실시간 검증쉬움어려움
코드량많음적음
권장대부분의 경우단순/파일 입력

요약

  • 제어 컴포넌트는 value + onChange 로 입력을 state와 일치시킨다.
  • 여러 입력은 name 속성과 [name]: value 로 한 핸들러에서 처리한다.
  • 체크박스는 checked, 셀렉트는 value 로 묶는다.
  • 제출은 onSubmit + e.preventDefault().
  • 비제어 컴포넌트는 useRef 로 제출 시점에만 값을 읽는다.

연습문제

  1. 이름·이메일·메시지 3개 입력을 가진 문의 폼을 하나의 핸들러로 처리하세요.
  2. "전체 동의" 체크박스 하나로 하위 체크박스 2개를 동시에 켜고 끄게 만드세요.
  3. 셀렉트로 고른 색상에 따라 박스 배경색이 바뀌도록 하세요.
  4. 제출 시 입력이 비어 있으면 "필수 항목입니다" 에러 메시지를 보여주세요.
  5. useRef 를 사용한 비제어 검색창을 만들어 제출 시 콘솔에 값을 출력하세요.

힌트 — 1번은 객체 state + [name]: value. 4번은 별도 error state를 두고 조건부 렌더링.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

React.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.