July 11, 2024
오늘은 ChatGPT에서 사용하는 파일 인풋을 유사하게 구현했습니다. ChatGPT에서는 파일을 입력할때마다 업로드를 하기 때문에 실제 처리 방식은 다르지만 파일들을 모아서 한번에 업로드 해야 한다고 가정했습니다. 즉, 여러 파일을 선택하고 폼에 누적해서 관리하는 기능을 구현했습니다. 아래에 ChatGPT의 인풋 컴포넌트를 가져왔습니다.
import React, { useState } from 'react'
import { useForm, Controller } from 'react-hook-form'
const FileInputForm = () => {
const { handleSubmit, control, setValue, getValues } = useForm()
const [filesArray, setFilesArray] = useState([])
const onSubmit = data => {
console.log('Submitted files:', getValues('files'))
}
const handleFileChange = event => {
const selectedFiles = Array.from(event.target.files)
if (selectedFiles) {
// Array.from에 넣는 이유는 selectedFiles가 array가 아닌 FileList이기 때문입니다.
const selectedFilesArray = Array.from(selectedFiles)
// 보여주는 파일 리스트 정보 처리
const newFileInfoList = selectedFilesArray.map(file => ({
name: file.name,
size: `${(file.size / 1024).toFixed(2)} KB`,
url: URL.createObjectURL(file),
}))
setFileInfoList(prevFileInfoList => [
...prevFileInfoList,
...newFileInfoList,
])
// files form value 처리
// 기존 파일 목록과 새로 선택한 파일 목록을 합칩니다.
// 이렇게 처리하지 않으면 새로 선택한 파일이 기존 파일을 대체합니다.
const currentFiles = Array.from(getValues('files') || [])
const updatedFiles = [...currentFiles, ...selectedFilesArray]
// array에 담긴 정보를 가지고 FileList를 만듭니다.
const fileList = new DataTransfer()
updatedFiles.forEach(file => fileList.items.add(file))
setValue('files', fileList.files)
}
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="files"
control={control}
defaultValue={[]}
render={() => (
<input type="file" onChange={handleFileChange} multiple />
)}
/>
<ul>
{filesArray.map((file, index) => (
<div key={index}>
<img src={file.url} alt={file.name} />
<li>{file.name}</li>
<li>{file.size}</li>
</div>
))}
</ul>
<button type="submit">Upload</button>
</form>
)
}
export default FileInputForm
ChatGPT를 사용할 때는 저런걸 어떻게 보여주지 생각했었는데 실제로 구현해보니까 신기했습니다. 보통 이렇게 누적식으로 관리해본 적은 처음이었고, n번째 파일을 제거할 때 DataTransfer api를 사용해서 FileList를 다시 만드는 게 인상적이었습니다.