[React] State로 사용자 입력 관리하기 - 2

Ahnzi·2024년 11월 19일

one-bite-react

목록 보기
4/11

저번 시간에 Register 컴포넌트를 만들어, 이름, 생년월일, 국적, 자기소개 네 가지를 입력받는 폼을 각각 생성하고, 입력된 정보를 별도의 state로 관리하는 방법을 살펴보았습니다.

하지만 코드를 다시 살펴보면, 비슷한 코드가 반복되고 있다는 점을 알 수 있습니다. 예를 들어, 모든 이벤트 핸들러에서 동일하게 상태 변화 함수를 호출하고, event.target.value를 인수로 전달하고 있습니다. 또한 4개의 state는 거의 동일한 방식으로 활용되고 있어 중복 코드가 많아 보입니다.

이번 시간에는 비효율적으로 보이는 코드를 보다 깔끔하고 간결하게 개선하는 작업을 진행해보겠습니다. 이를 위해 이름, 생년월일, 국적, 자기소개와 같은 4개의 state를 하나의 객체로 묶어 관리하도록 해보겠습니다.

모든 유저의 상태를 저장하기 위해 input이라는 state를 생성합니다. 초기값으로는 객체를 설정하며, 이 객체에는 이름, 생년월일, 국적, 자기소개를 각각의 state로 포함합니다.

[Register.jsx]

 import { useState } from "react";

const Register = () => {
  const [input, setInput] = useState({
    name: "",
    birth: "",
    country: "",
    bio: "",
  });
};

export default Register;

이벤트 핸들러도 같이 수정합니다.

우선 onChangeName 이벤트 핸들러를 수정합니다. 이제 setName() 함수 대신 setInput() 함수를 사용합니다. setInput() 함수에는 새로운 상태 값을 전달해야 하므로, 기존 객체를 복사한 후 name 프로퍼티의 값을 event.target.value로 업데이트한 객체를 전달합니다. 이는 onChangeName 함수가 실행될 때, 사용자가 입력한 값으로 name 프로퍼티를 수정하기 위해서입니다.

또한, 스프레드 연산자를 사용하여 기존 input 상태에 있던 프로퍼티 값들(예: birth, country, bio)은 변경하지 않고 그대로 유지되도록 처리합니다. 스프레드 연산자를 사용하지 않으면 input 상태 값이 새 객체로 덮어써지면서 기존에 입력했던 birth, country, bio 프로퍼티가 사라질 수 있습니다. 따라서, 스프레드 연산자를 활용해 기존 값은 유지하고, 변경하려는 프로퍼티의 값만 업데이트하도록 코드를 작성해야 합니다.

[Register.jsx]

import { useState } from "react";

const Register = () => {
  const [input, setInput] = useState({
    name: "",
    birth: "",
    country: "",
    bio: "",
  });

  const onChangeName = (event) => {
    setInput({
      ...input,
      name: event.target.value,
    });
  };
};

export default Register;

나머지 이벤트 핸들러도 수정합니다.

[Register.jsx]

import { useState } from "react";

const Register = () => {
  const [input, setInput] = useState({
    name: "",
    birth: "",
    country: "",
    bio: "",
  });

  const onChangeName = (event) => {
    setInput({
      ...input,
      name: event.target.value,
    });
  };

  const onChangeBirth = (event) => {
    setInput({
      ...input,
      birth: event.target.value,
    });
  };

  const onChangeCountry = (event) => {
    setInput({
      ...input,
      country: event.target.value,
    });
  };
  const onChangeBio = (event) => {
    setInput({
      ...input,
      bio: event.target.value,
    });
  };
};

export default Register;

JSX도 수정합니다.

[Register.jsx]

import { useState } from "react";

const Register = () => {
  const [input, setInput] = useState({
    name: "",
    birth: "",
    country: "",
    bio: "",
  });

  const onChangeName = (event) => {
    setInput({
      ...input,
      name: event.target.value,
    });
  };

  const onChangeBirth = (event) => {
    setInput({
      ...input,
      birth: event.target.value,
    });
  };

  const onChangeCountry = (event) => {
    setInput({
      ...input,
      country: event.target.value,
    });
  };
  const onChangeBio = (event) => {
    setInput({
      ...input,
      bio: event.target.value,
    });
  };

  return (
    <div>
      <div>
        <input value={input.name} onChange={onChangeName} placeholder={"이름"} />
      </div>
      <div>
        <input type="date" value={input.birth} onChange={onChangeBirth} />
      </div>
      <div>
        <select value={input.country} onChange={onChangeCountry}>
          <option value=""></option>
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
        </select>
      </div>
      <div>
        <textarea value={input.bio} onChange={onChangeBio} />
      </div>
    </div>
  );
};

export default Register;

위 코드의 핵심은 input 객체의 프로퍼티 중 하나를 변경하더라도, 변경하지 않은 다른 프로퍼티의 값은 그대로 유지된다는 점입니다.

이벤트 핸들러를 하나의 객체에서 통합적으로 관리하기 위해 단일 이벤트 핸들러를 만들어줍니다. 이를 통해 각 입력 필드에 개별적으로 이벤트 핸들러를 작성할 필요 없이, 통합된 핸들러로 효율적으로 상태를 관리할 수 있습니다.

[Register.jsx]

import { useState } from "react";

const Register = () => {
  const [input, setInput] = useState({
    name: "",
    birth: "",
    country: "",
    bio: "",
  });

  const onChange = (event) => {
    setInput({
      ...input,
      [event.target.name]: event.target.value,
    });
  };

  return (
    <div>
      <div>
        <input name="name" value={input.name} onChange={onChange} placeholder={"이름"} />
      </div>
      <div>
        <input type="date" name="birth" value={input.birth} onChange={onChange} />
      </div>
      <div>
        <select name="country" value={input.country} onChange={onChange}>
          <option value=""></option>
          <option value="kr">한국</option>
          <option value="us">미국</option>
          <option value="uk">영국</option>
        </select>
      </div>
      <div>
        <textarea name="bio" value={input.bio} onChange={onChange} />
      </div>
    </div>
  );
};

export default Register;

onChange

  const onChange = (event) => {
    setInput({
      ...input,
      [event.target.name]: event.target.value,
    });
  };

onChange 함수를 살펴보겠습니다. 이 함수가 호출되면 setInput 함수를 호출하며, 인수로 새 객체를 전달합니다. 먼저 스프레드 연산자를 사용해 기존 input 값을 복사한 뒤, 업데이트할 프로퍼티의 Key를 동적으로 설정합니다. 이를 위해 대괄호 표기법([ ])을 사용하며, 대괄호 안에 event.target.name을 넣습니다.

대괄호 표기법을 사용하면, event.target.name에 저장된 값이 프로퍼티의 Key로 설정됩니다. 즉, 이벤트가 발생한 태그의 name 속성 값이 Key로 사용됩니다.

예를 들어, 사용자가 생년월일을 입력하면 두 번째 input 태그에서 이벤트가 발생합니다. 이때, event.target은 해당 input 태그를 가리키며, name 속성 값은 "birth"입니다. 따라서 [event.target.name]은 "birth"가 되고, 결국 객체는 birth: event.target.value로 업데이트됩니다.

profile
운동하는 개발자 Ahnzi 입니다.

0개의 댓글