모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

9강 / 전체 30강

리스트 렌더링과 key

5분 읽기 조회 4

제어 컴포넌트 패턴으로 폼을 구현하는 방법, 다양한 입력 요소 처리, 폼 유효성 검사, 제출 처리까지 실전 폼 구현을 배웁니다.

📝 폼 처리의 두 가지 방식

리액트에서 폼(form)을 처리하는 방식은 크게 두 가지입니다. 제어 컴포넌트(Controlled Component)와 비제어 컴포넌트(Uncontrolled Component)입니다. 이 강의에서는 리액트의 표준 방식인 제어 컴포넌트를 집중적으로 다룹니다.

구분제어 컴포넌트비제어 컴포넌트
상태 관리React stateDOM 참조 (useRef)
값 접근state 변수ref.current.value
검증 시점입력 즉시 가능제출 시점만
사용 빈도대부분의 경우파일 업로드, 제3자 라이브러리 통합

🎛️ 제어 컴포넌트 — 실전 패턴

제어 컴포넌트란 value가 React state로 관리되는 입력 요소입니다. 입력값이 바뀔 때마다 state가 업데이트되고, state가 곧 입력창에 표시되는 값이 됩니다. state가 "단일 진실의 원천(single source of truth)"이 됩니다.

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

  return (
    <div>
      {/* value={name}: 상태가 입력값을 제어 */}
      {/* onChange: 사용자 입력 → 상태 업데이트 */}
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="이름 입력"
      />
      <p>입력된 이름: {name}</p>
    </div>
  )
}

📑 여러 필드를 하나의 state로 관리

회원가입 폼처럼 여러 필드가 있을 때, 필드마다 useState를 만들 수도 있지만 객체 하나로 묶어 관리하면 더 편합니다:

interface SignupForm {
  name: string
  email: string
  password: string
  agree: boolean
}

function SignupPage() {
  const [form, setForm] = useState<SignupForm>({
    name: '',
    email: '',
    password: '',
    agree: false,
  })

  // 재사용 가능한 범용 핸들러
  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const { name, value, type, checked } = e.target
    setForm((prev) => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }))
  }

  return (
    <form>
      <input name="name" value={form.name} onChange={handleChange} placeholder="이름" />
      <input name="email" type="email" value={form.email} onChange={handleChange} placeholder="이메일" />
      <input name="password" type="password" value={form.password} onChange={handleChange} placeholder="비밀번호" />
      <label>
        <input name="agree" type="checkbox" checked={form.agree} onChange={handleChange} />
        이용약관 동의
      </label>
    </form>
  )
}

[name]: value는 컴퓨티드 프로퍼티 이름(computed property name)입니다. 입력 요소의 name 속성과 state 키가 일치하면 하나의 핸들러로 모든 필드를 처리할 수 있습니다.

✅ 폼 유효성 검사

제출 전에 입력값을 검증해야 합니다. 간단한 검증은 직접 구현합니다:

function LoginForm() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [errors, setErrors] = useState<Record<string, string>>({})

  function validate(): boolean {
    const newErrors: Record<string, string> = {}
    if (!email) newErrors.email = '이메일을 입력하세요'
    else if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) newErrors.email = '올바른 이메일 형식이 아닙니다'
    if (!password) newErrors.password = '비밀번호를 입력하세요'
    else if (password.length < 8) newErrors.password = '8자 이상 입력하세요'
    setErrors(newErrors)
    return Object.keys(newErrors).length === 0
  }

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    if (!validate()) return  // 검증 실패 시 제출 중단
    // API 호출
    console.log('로그인:', email, password)
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input value={email} onChange={(e) => setEmail(e.target.value)} type="email" />
        {errors.email && <p className="text-red-500 text-sm">{errors.email}</p>}
      </div>
      <div>
        <input value={password} onChange={(e) => setPassword(e.target.value)} type="password" />
        {errors.password && <p className="text-red-500 text-sm">{errors.password}</p>}
      </div>
      <button type="submit">로그인</button>
    </form>
  )
}

🎯 select와 textarea

select와 textarea도 제어 컴포넌트로 동일하게 처리합니다:

function FeedbackForm() {
  const [category, setCategory] = useState('bug')
  const [message, setMessage] = useState('')

  return (
    <form>
      {/* select — value와 onChange로 제어 */}
      <select value={category} onChange={(e) => setCategory(e.target.value)}>
        <option value="bug">버그 신고</option>
        <option value="feature">기능 요청</option>
        <option value="other">기타</option>
      </select>

      {/* textarea — 동일한 방식 */}
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        rows={5}
        placeholder="내용을 입력하세요"
      />
    </form>
  )
}

✅ 정리

  • 제어 컴포넌트: value를 state로 관리하고 onChange로 업데이트합니다.
  • 여러 필드는 객체 state + [name] computed key로 하나의 핸들러가 처리합니다.
  • onSubmit에서 e.preventDefault() 후 검증 → API 호출 순서로 진행합니다.
  • select와 textarea도 동일한 제어 패턴을 사용합니다.

관련 주제

  • map으로 리스트 렌더링
  • key 속성 역할
  • key 주의점
  • 동적 목록 생성
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 리액트 입문 — 컴포넌트로 만드는 웹
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

리액트 입문 — 컴포넌트로 만드는 웹

이 강의가 속한 시리즈는 총 30강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...