✅ File Input 다루는 법

<input type="file" />

  • 리액트 앱에서 다루기 어려운 것 중 하나가 바로 form.
  • form 안의 input 값의 상태를 일일히 관리해주어야 하고, 각각 validation까지 해준다면 더욱 복잡해짐
  • React Hook Form을 통해서 한번에 form 안의 모든 input 값들을 관리할 수 있음.
  • 그러나 file 타입의 input 값을 가져오는 일은 쉽지 않음.

| 파일 타입의 인풋은 애플리케이션 계층에서 관리가 되어야 합니다. 파일 선택을 취소할 수도 있고 FileList 객체도 있기 때문입니다. (출처: react-hook-form 공식 문서)

  • 위와 같은 이유로 react-hook-form을 사용한 다른 input들과 같은 방식으로 file input을 작성할 수 없었음
  • 그리고 아래 이유로 value와 onChange 등을 이용하여 file input에 들어온 값을 바로 가져오기도 힘듦

| React에서 <input type="file" />은 프로그래밍적으로 값을 설정 할 수 없고 사용자만이 값을 설정할 수 있기 때문에 항상 비제어 컴포넌트임 (출처 - react 공식 문서)

  • 실제로 React 공식문서에서 file input은 아래와 같이 ref를 걸어 비제어 컴포넌트로 사용하고 있음
class FileInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.fileInput = React.createRef();
  }
  handleSubmit(event) {
    event.preventDefault();
    alert(`Selected file - ${this.fileInput.current.files[0].name}`);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Upload file:
          <input type="file" ref={this.fileInput} />
        </label>
        <br />
        <button type="submit">Submit</button>
      </form>
    );
  }
}
  • React Hook Form의 watch는 인자로 원하는 input 이름을 넣어주면, 해당 input의 값을 관찰하여 값이 바뀔 때 업데이트된 값을 즉시 알아챌 수 있음
// Upload.tsx

const Upload = () => {
  const { register, handleSubmit, setValue, watch } = useForm<FeedToUpload>();
  const watchThumbnailImage = watch("thumbnailImage");

  return (
    // ...
    <FileInput
      fileName={watchThumbnailImage?.name}
      onChange={(event) =>
        setValue("thumbnailImage", event.currentTarget.files[0])
      }
    />
    // ...
  );
};
// FileInput.tsx
const FileInput = ({ fileName, ...options }: Props) => {
  return (
    <Styled.Root>
      <Styled.Label>
        <input type="file" {...options} />
        <span>파일 선택</span>
      </Styled.Label>
      <Styled.FileNameText>
        {fileName || "파일을 선택해주세요."}
      </Styled.FileNameText>
    </Styled.Root>
  );
};

참고