누구나 로고
オンライン講座企業・団体研修ライブラリトレンドインサイト無料コースお知らせお問い合わせ採用サイト
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌리액트 입문 — 컴포넌트로 만드는 웹9강

9강 / 전체 28강

리스트 렌더링과 key

5분 읽기 조회 1

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

📝 폼 처리의 두 가지 방식

리액트에서 폼(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
  • 누구나
이전 8강조건부 렌더링다음 10강 폼 다루기

댓글

0/1000

불러오는 중...

목차

  1. 1.리액트란 무엇인가
  2. 2.개발 환경 만들기 — Vite
  3. 3.JSX 기초
  4. 4.컴포넌트 만들기
  5. 5.Props로 데이터 전달
  6. 6.State와 useState
  7. 7.이벤트 처리
  8. 8.조건부 렌더링
  9. 9.리스트 렌더링과 key
  10. 10.폼 다루기
  11. 11.useEffect 기초
  12. 12.데이터 가져오기(fetch)
  13. 13.상태 끌어올리기
  14. 14.컴포넌트 합성과 재사용
  15. 15.useRef와 DOM 접근
  16. 16.커스텀 훅 만들기
  17. 17.Context API
  18. 18.useReducer
  19. 19.리스트 앱 실전 ①
  20. 20.리스트 앱 실전 ②
  21. 21.라우팅 — React Router
  22. 22.스타일링 방법들
  23. 23.리스트·상세 페이지 연결
  24. 24.비동기 상태 관리 패턴
  25. 25.성능 기초 — memo
  26. 26.에러 처리와 경계
  27. 27.환경변수와 빌드
  28. 28.배포하기

カスタマーサポート

  • お知らせ
  • よくある質問
  • お問い合わせ
  • コミュニティ

ご利用案内

  • 利用規約
  • プライバシーポリシー
  • 返金ポリシー

サービス

  • 会社紹介
  • 新規登録
  • 新着コース
  • 無料コース
누구나 로고
利用規約プライバシーポリシー返金ポリシー

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증관리자
ホームコースライブラリお問い合わせMY