React에서 FormData 사용하기

먼지·2022년 9월 19일
1

React

목록 보기
2/8
post-thumbnail

전에 트위터에서 봤었고 탐토님이랑 리믹스 실습하면서도 봤는데 어제 한 번 다시 html 웹 표준을 얘기하다 리액트에서 form input 값들을 다룰 때 state랑 ref 훅이나 react hook form 라이브러리를 사용하면서 고통받을 필요 없다고 하셔서 마침 게시판을 만드는 김에 배우려고 한다..!

Controlled vs Uncontrolled Components

참고
제어 컴포넌트 vs. 비제어 컴포넌트
제어 컴포넌트와 비제어 컴포넌트의 차이점

제어 컴포넌트 (Controlled Component)<input>, <textarea>, <select>와 같은 HTML 폼 엘리먼트를 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트하는 것으로, 대부분 경우엔 폼을 구현하는데 제어 컴포넌트를 사용하는 것이 좋다고 공식 문서에 나와있다. 리액트에 의해 값이 제어되므로 제어 컴포넌트로 부르는 것

비제어 컴포넌트는 바닐라 자바스크립트처럼 폼을 제출할 때 (onSubmit) form input 내부의 값을 얻어오는 방식으로 ref를 사용해서 값을 얻는데 값이 실시간을로 동기화되지 않아서 컴포넌트 내부 값의 변화를 즉각적으로 대응할 수 없다.

핵심은 어떤 방식이 좋다 나쁘다가 아니라 언제 제어 컴포넌트와 비제어 컴포넌트를 사용하는 경우인지를 알아야 하는 것 같은데 그럼 formData는 ref를 이용하는 비제어 컴포넌트와 비슷하다고 볼 수 있는 건가??

useRef를 이용하던 코드

export default function Editor() {
  const catRef = React.useRef() as React.RefObject<HTMLSelectElement>;
  const titRef = React.useRef() as React.RefObject<HTMLInputElement>;
  
  ...
  
  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    const category = catRef.current?.value;
    const title = titRef.current?.value;
    const instance = editorRef.current?.getInstance();
    const data = instance?.getHTML();
    console.log(category, title, data);
  };

  return (
    <form onSubmit={onSubmit}>
      <div>
        <select ref={catRef}>
          <option value="">카테고리 선택</option>
          <option value="free">자유게시판</option>
          <option value="share">정보공유</option>
          <option value="activity">활동모집</option>
        </select>
        <input
          ref={titRef}
          className="outline-none p-2"
          placeholder="글 제목을 입력해 주세요"
        />
      </div>
      <ToastEditor ... />
    </div>
  );
}

FormData

MDN - FormData

처음엔 formElemRef를 만들어서 const formData = new FormData(formElemRef); 이렇게 도전했는데 안 돼서 전에 봤던 트위터 글을 찾아서 적용했다.
https://twitter.com/DavidKPiano/status/1569361453928300545
첨부터 이걸 봤어야 했는데..!

방법은 그냥 input에 name 속성을 달아주는 거라 쉬웠는데 뒤에 생각해 보니 카테고리 선택을 안 했거나 제목을 입력하지 않았거나 글자 수 제한 등을 체크하려면 onSubmit 함수 안이나 input에 pattern, maxLength 등을 사용해야 하니 이것도 나름 고충? 이 있겠지만 ref나 state를 여러 개 만들어도 되지 않아서 좋은 것 같다.

export default function Editor() {
  ...
  
  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    // e.target은 빨간줄 떴음..
    const formData = new FormData(e.currentTarget);
    console.log(formData.get('tit-input'));
    const data = Object.fromEntries(formData);
    console.log('data', data);
  };

  return (
    <form onSubmit={onSubmit}>
      <div>
        <select name="cat-input">
          <option value="">카테고리 선택</option>
          <option value="free">자유게시판</option>
          <option value="share">정보공유</option>
          <option value="activity">활동모집</option>
        </select>
        <input
          name="tit-input"
          placeholder="글 제목을 입력해 주세요"
        />
      </div>
      <ToastEditor ... />
    </div>
  );
}

이런 식으로 백엔드에 데이터를 보내면 될 것 같은데, 유효성 검사는 어떻게 하면 좋을지 생각해 봐야겠다.

  const handleCreateBoard = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);
    const instance = editorRef.current?.getInstance();
    const content = instance?.getHTML();
    const newData = {
      ...data,
      imgList,
      content,
    };
    console.log('newData:', newData);
    // category: 'free';
    // content: '<p>본문!</p>';
    // imgList: [];
    // title: '제목';
  };
profile
꾸준히 자유롭게 즐겁게

0개의 댓글