[React] Form (폼)

문지은·2023년 7월 18일
1

React

목록 보기
10/24
post-thumbnail

Form

  • 사용자로부터 입력을 받기 위해 사용하는 것
    • 텍스트 입력, 체크박스, 셀렉트 등 사용자가 선택해야 하는 모든 것
  • 아래와 같은 HTML 폼도 리액트에서 잘 작동 하지만 자바스크립트 코드를 통해 사용자가 입력한 값에 접근하기는 매우 불편
<form>
  <labal>
    이름:
    <input type="text" name="name" />
  </label>
  <button type="submit">제출</button>	
</form>
  • 리액트에서는 사용자가 입력한 값에 접근하고 제어할 수 있도록 제어 컴포넌트(Controlled Components) 사용

제어 컴포넌트

  • 사용자가 입력한 값에 접근하고 제어할 수 있도록 해주는 컴포넌트
  • HTML 폼은 각 엘리먼트가 자체적으로 state를 관리
    • <input>, <textarea>, <select> 등이 각각 내부에 state를 가지고 있음
    • 자바스크립트 코드를 통해 각각의 값에 접근하기가 쉽지 않음
  • 제어 컴포넌트에서는 모든 데이터를 state에서 관리
    • state 값을 변경하기 위해서는 무조건 setState() 함수를 사용해야 함
    • 함수 컴포넌트에서는 useState() 훅을 사용하여 state 관리
  • 위에서 작성했던 사용자의 이름을 입력 받는 HTML 폼을 리액트의 제어 컴포넌트로 만들면 다음과 같음
    • input 태그의 value={value}에서 리액트 컴포넌트의 state에 들어 있는 값을 input에 항상 표시
    • 입력 값이 변경되면 onChange={handleChange}에서 handleChange 함수 호출
      • setValue() 함수를 사용하여 새롭게 변경된 값을 value라는 이름의 state에 저장
    • event는 이벤트 객체를 나타네며 event.target은 현재 발생한 이벤트의 타겟, event.target.value는 해당 타겟의 value 속성값을 의미
      • 이 때 targetinput 엘리먼트
import React, {useState} from 'react';

function NameForm(props) {
    const [value, setValue] = useState('');

    const handleChange = (event) => {
        setValue(event.target.value);
    }

    const handleSubmit = (event) => {
        alert('입력한 이름: ' + value);
        event.preventDefault();
    }

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

export default NameForm
  • 실행 결과

  • 이처럼 제어 컴포넌트를 사용하면 입력값이 리액트 컴포넌트의 state를 통해 관리되고, 여러 개의 입력 양식 값을 원하는대로 조종 가능
    • 예를 들어 사용자가 입력한 모든 알파벳을 대문자로 변경시켜서 관리하고 싶다면 아래 코드와 같이 작성하면 됨
const handleChange = (event) => {
    setValue(event.target.value.toUpperCase());
}

textarea 태그

  • 여러 줄에 걸쳐서 나올 정도의 긴 텍스트를 입력받기 위한 HTML 태그
  • HTML 에서는 아래와 같이 텍스트를 태그가 감싸는 형태로 사용
<textarea>
  이곳에 긴 텍스트가 들어가게 됩니다.
</textarea>
  • 리액트에서는 <textarea> 태그에 value라는 attribute를 사용하여 텍스트를 표시
    • 제어 컴포넌트 방식이므로 값을 컴포넌트의 state를 사용해서 다룰 수 있음
  • 아래 코드는 고객으로부터 요청사항을 입력받기 위한 RequestForm 이라는 컴포넌트
    • state로는 value가 있고, 이 값을 <textarea> 태그의 value라는 attribute에 넣어줌으로써 화면에 나타나게 됨
    • 여기에서는 value를 선언할 때 초깃값을 넣어줬기 때문에 처음 렌더링될 때부터 <textarea>에 텍스트가 나타남
import React, {useState} from 'react';

function RequestForm(props) {
    const [value, setValue] = useState('요청사항을 입력하세요.');

    const handleChange = (event) => {
        setValue(event.target.value);
    }

    const handleSubmit = (event) => {
        alert('입력한 요쳥사항: ' + value);
        event.preventDefault();
    }

    return (
        <form onSubmit={handleSubmit}>
            <label>
                요청사항:
                <textarea value={value} onChange={handleChange} />
            </label>
            <button type="submit">제출</button>
        </form>
    )
}

export default RequestForm
  • 실행 결과

select 태그

  • 드롭다운(drop-down) 목록을 보여주기 위한 HTML 태그
    • 여러 가지 옵션 중에서 하나를 선택할 수 있는 기능을 제공
  • HTML 에서는 아래 코드와 같이 <option> 태그를 <select> 태그가 감싸는 형태로 사용
    • 현재 선택된 옵션의 경우 selected라는 attribute를 가지고 있음
<select>
    <option value="apple">사과</option>
    <option value="banana">바나나</option>
    <option selected value="grape">포도</option>
    <option value="watermelon">수박</option>
</select>
  • 리액트의 제어 컴포넌트로 만들면 다음과 같음
    • FruitSelect라는 컴포넌트의 state로 grape라는 초깃값을 가진 value 설정
    • handleChange() 함수에서 setValue() 함수를 사용하여 값을 업데이트
import React, {useState} from 'react';

function FruitSelect(props) {
    const [value, setValue] = useState('grape');

    const handleChange = (event) => {
        setValue(event.target.value);
    }

    const handleSubmit = (event) => {
        alert('선택한 과일: ' + value);
        event.preventDefault();
    }

    return (
        <form onSubmit={handleSubmit}>
            <label>
                과일을 선택하세요:
                <select value={value} onChange={handleChange}>
                    <option value="apple">사과</option>
                    <option value="banana">바나나</option>
                    <option value="grape">포도</option>
                    <option value="watermelon">수박</option>
                </select>
            </label>
            <button type="submit">제출</button>
        </form>
    )
}

export default FruitSelect
  • 실행 결과
    • 초기값으로 포도가 설정되어 있음

  • 만약 목록에서 다중으로 선택이 되도록 하려면 아래와 같이 multiple라는 속성 값을 true로 하고, value로 선택된 옵션의 값이 들어있는 배열을 넣어주면 됨
<select multiple={true} value={['B', 'C']}>

File input 태그

  • 디바이스의 저장 장치로부터 사용자가 하나 또는 여러 개의 파일을 선택할 수 있게 해주는 HTML 태그
  • 서버로 파일을 업로드하거나 자바스크립트의 File API를 사용해서 파일을 다룰 때 사용
  • 아래와 같이 <input> 태그를 사용하고 타입을 file로 해주면 됨
    • File input 태그는 그 값이 읽기 전용이기 때문에 리액트에서는 비제어 컴포넌트가 됨
<input type="file" />

여러 개의 입력 다루기

  • 하나의 컴포넌트에서 여러 개의 입력을 다루기 위해서는 어떻게 해야할까?
    • 여러 개의 state를 선언하여 각각의 입력에 대해 사용하면 됨
  • 아침 식사 선택 유무를 checkbox type으로, 방문객 수를 number type으로 제출할 수 있는 Reservation 컴포넌트를 작성해보자.
import React, {useState} from 'react';

function Reservation(props) {
    const [haveBreakfast, setHaveBreakfast] = useState(true);
    const [numberOfGuest, setNumberOfGuest] = useState(2);

    const handleSubmit = (event) => {
        alert(`아침식사 여부:  ${haveBreakfast}, 방문객 수: ${numberOfGuest}`);
        event.preventDefault();
    }

    return (
        <form onSubmit={handleSubmit}>
            <label>
                아침식사 여부:
                <input type="checkbox" checked={haveBreakfast} 
                    onChange={(event) => {
                        setHaveBreakfast(event.target.checked)
                    }} />
            </label>
            <br />
            <label>
                방문객 수:
                <input type="number" value={numberOfGuest} 
                    onChange={(event) => {
                        setNumberOfGuest(event.target.value)
                    }} />
            </label>
            <button type="submit">제출</button>
        </form>
    )
}

export default Reservation
  • 실행결과

Input Null Value

  • 앞에서 배운 것 처럼 제어 컴포넌트에 value prop을 정해진 값으로 넣으면 코드를 수정하지 않는 한 입력값을 바꿀 수 없다.
  • 만약 value prop은 넣되 자유롭게 입력할 수 있게 만들고 싶다면 앞에 undefined 또는 null을 넣어주면 된다.
  • 아래 코드를 예로 들면,
    • 처음에는 input 값이 hi로 정해져있어서 값을 바꿀 수 없는 입력 불가 상태였다가 timer에 의해 1초 뒤에 value가 null인 input 태그가 렌더링되면서 입력 가능한 상태로 바뀐다.
ReactDOM.render(<input value="hi" />, rootNode);

setTime(function() {
    ReactDOM.render(<input value={null} />, rootNode);
}, 1000);
  • 이러한 방법을 잘 활용하면 value prop을 넣으면서 동시에 사용자가 자유롭게 입력할 수 있게 만들 수 있다.

실습 - 사용자 정보 입력받기

  • <input> 태그의 text 타입으로 이름을 입력 받고, <select> 태그로 성별을 입력받는 SignUp 컴포넌트를 작성해보자.
import React, { useState } from "react";

function SignUp(props) {
    const [name, setName] = useState("");
    const [gender, setGender] = useState("남자");

    const handleChangeName = (event) => {
        setName(event.target.value);
    };

    const handleChangeGender = (event) => {
        setGender(event.target.value);
    };

    const handleSubmit = (event) => {
        alert(`이름: ${name}, 성별: ${gender}`);
        event.preventDefault();
    };

    return (
        <form onSubmit={handleSubmit}>
            <label>
                이름:
                <input type="text" value={name} onChange={handleChangeName} />
            </label>
            <br />
            <label>
                성별:
                <select value={gender} onChange={handleChangeGender}>
                    <option value="남자">남자</option>
                    <option value="여자">여자</option>
                </select>
            </label>
            <button type="submit">제출</button>
        </form>
    );
}

export default SignUp;
  • 실행 결과

실습 전체 코드

References

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

글이 많은 도움이 되었습니다, 감사합니다.

답글 달기