본문으로 바로가기

[React] input tag onChange object 인 경우 간단하고 깔끔하게 사용하기

우연히 다른 사람의 코드를 보았는데 확장성 없게(?) 구현이 되어 있길래 가르쳐 주면서 블로그에도 기록해두려고 한다. (작성하는 코드는 전체 코드가 아닌 이해하는데 문제없도록 간단하게 구현하였습니다. 따라서 그대로 복붙하면 실행되지 않습니다.)

input onChange 사용하기

React 입문할 때 state를 배우고, input element를 이용하여 state를 변경하는 방법을 배우곤 한다. 이때 tate 값이 object인 경우 보통 아래 처럼 구현을 많이 한다. (실제 인터넷 예시에도 저렇게 많이 되어 있다.)

const [obj, setObj] = useState({ name: "" });

<input value={obj.name} onChange={e => setObj({ name: e.target.value })} />;

위 코드가 문제 있어보이나요? 전혀 문제 있어보이지 않습니다. 그렇다면 obj state에 key: value 씽을 더 추가하면 어떻게 구현해야할까요? 아래 코드를 확인해봅시다.

const [obj, setObj] = useState({ name: "", address: "" });

<input
  value={obj.name}
  onChange={e => setObj(prev => ({ ...prev, name: e.target.value }))}
/>
<input
  value={obj.address}
  onChange={e => setObj(prev => ({ ...prev, address: e.target.value }))}
/>

대부분 개발자들이 위와 같이 구현할 것입니다. 위 코드도 문제는 없습니다. ... 문법, prevState 개념 모두 알고 있으니깐 구현 잘한 코드라고 볼 수 있겠죠. 그러나 딱 위 정도의 코드만 구현할 수 있으면 문제가 생길 수도 있습니다. 만약 obj state에 key: value 쌍이 10개 이상이면 어떻게 될까요? 구현하기 전 상상 코딩을 해봅시다. input element를 10개, 그 안에 onChange 함수 10개를 작성하여 끝낼 것이고 나중에 최적화를 한다고 onChange 함수를 밖으로 빼 useCallback으로 감싸면 대충 아래 코드 형식이 나올 것입니다. (useCallback 제외)

const [obj, setObj] = useState({
  name1: "",
  name2: "",
  name3: "",
  name4: "",
  name5: "",
  name6: "",
  name7: "",
  name8: "",
  name9: "",
  name10: "",
});

const onChangeName1 = e => setObj(prev => ({ ...prev, name1: e.target.value }));
const onChangeName2 = e => setObj(prev => ({ ...prev, name2: e.target.value }));
const onChangeName3 = e => setObj(prev => ({ ...prev, name3: e.target.value }));
...
const onChangeName8 = e => setObj(prev => ({ ...prev, name8: e.target.value }));
const onChangeName9 = e => setObj(prev => ({ ...prev, name9: e.target.value }));
const onChangeName10 = e => setObj(prev => ({ ...prev, name10: e.target.value }));

<input
  value={obj.name1}
  onChange={onChangeName1}
/>
<input
  value={obj.name2}
  onChange={onChangeName1}
/>
...
<input
  value={obj.name9}
  onChange={onChangeName1}
/>
<input
  value={obj.name10}
  onChange={onChangeName1}
/>

10개를 구현해도 위와 같은데 100개를 구현하면 벌써부터 귀찮아 지고 이건 아니다 싶습니다. 그렇다면 어떻게 해야할까요? name 속성을 사용하여 onChange 함수를 만들면 됩니다. 아래 코드를 참고해봅시다.

<input name="name1" value={obj.name1} onChange={onChange}/>
<input name="name2" value={obj.name2} onChange={onChange}/>

이전 input element와 약간의 차이점이 보이시나요? 각각의 input element의 name 속성에 obj state의 key 값을 넣어주었습니다. 그리고 모든 onChange 함수를 1개의 onChange 함수로 변경해주었습니다. 그렇다면 onChange 함수 1개로 모든 input element를 컨트롤 할 수 있다는 것인데 어떻게 구현하면 될까요? 아래 onChange 함수를 확인해봅시다.

const onChange = e => {
  const {
    target: { value, name }
  } = e;

  setObj(prev => ({ ...prev, [name]: value }));
};

위와 같이 구현하면 됩니다. name, value값을 e.target (input element)에서 가져와 해당 key의 value만 변경해준 것입니다. 이전에 저희가 name 속성의 값을 obj의 key로 넣어주었기 때문에 name === key 이므로 이렇게 구현할 수 있습니다. 그렇다면 최종 코드는 아래와 같이 될 것 입니다. (코드 줄였습니다.)

const [obj, setObj] = useState({
  name1: "",
  name2: "",
  name3: ""
});

const onChange = e => {
  const {
    target: { value, name }
  } = e;

  setObj(prev => ({ ...prev, [name]: value }));
};

return (
  <>
    <input name="name1" value={obj.name1} onChange={onChange}/>
    <input name="name2" value={obj.name2} onChange={onChange}/>
    <input name="name3" value={obj.name3} onChange={onChange}/>
  </>
)

위와 같이 구현하는 코드 가독성도 좋아지고 간결해졌습니다. 솔직히 2, 3개까지는 그냥 onChange를 각각 선언하여 사용하여도 되지만 그 보다 많아지면 위와 같이 최적화하여도 좋을 것 같습니다. 알고 사용하지 않는 것과 모르고 못 사용하는 것은 큰 차이가 있는 것 같습니다.

마지막

해당 내용은 틀릴 수도 있다는 것을 감안하여 봐주세요. 틀린 내용 및 오탈자 수정 요청 환영입니다.

반응형