https://next-s3-upload.codingvalue.com/
nextjs에서 s3에 가장 간단하게 파일을 업로드 할 수 있는 라이브러리 next s3 upload를 활용하여 다중 파일 업로드 기능을 구현해보았다.
AWS SDK보다 코드가 간결하고, 뭣보다 예시가 잘 작성되어있어 빠르게 해당 라이브러리를 익힐 수 있는 것이 매우 큰 장점이다.
import { useState } from 'react';
import { useS3Upload } from 'next-s3-upload';
export default function UploadImages() {
const [urls, setUrls] = useState([]);
const { uploadToS3 } = useS3Upload();
const handleFilesChange = async ({ target }) => {
const files = Array.from(target.files);
for (let index = 0; index < files.length; index++) {
const file = files[index];
const { url } = await uploadToS3(file);
setUrls(current => [...current, url]);
}
};
return (
<div>
<input
type="file"
name="file"
multiple="multiple"
onChange={handleFilesChange}
/>
<div>
{urls.map((url, index) => (
<div key={url}>
File {index}: ${url}
</div>
))}
</div>
</div>
);
}
next s3 upload 문서에서 다중 선택 기능을 어떻게 처리하는지 보니, fileList를 반복문 안에 돌려 각 file을 하나하나씩 s3에 업로드해주는 방식으로 처리하였다.
기본적으로 next s3 upload는 단건 처리로 구성되어있다보니, 이와같이 multi upload의 경우 반복문으로 처리하는 것으로 보인다.
또한, 앞서 말했듯이 next s3 upload가 단건 처리를 기준으로 하다보니...next s3 upload에서 제공해주는 FileInput
도 직접 만들어야한다는 단점이 있다..😟
하지만 금방 만들 수 있기에 단점이 아닐지도..?
const useFileUpload = () => {
const { uploadToS3 } = useS3Upload()
// 다중건 처리
const multiUpload = async ({ target }) => {
const { files } = target
const fileList = Array.from(files)
const finishedList: Promise<T>[] = []
fileList.forEach((file) => {
const uploadedFile = uploadToS3(file)
finishedList.push(uploadedFile)
})
return Promise.all(finishedList)
}
return { multiUpload }
}
우선 다중건들은 fileList라는 array같은 형태의 리스트가 전달이 된다.
근데 그건 array같은거지 진짜 array는 아니기때문에 Array.from
을 사용해서 array형태로 바꿔준다.
또한 for문을 돌리면 비동기 처리가 너무 지연이 되기 대문에 forEach를 사용해서 promise형태로 반환받도록 처리했다.
처리가 완료된 배열이 넘겼을 때 promise가 해제될 수 있도록 Promise.all
로 감싸 처리했다.
비동기를 사용한 이유
파일 크기가 우후죽순이고 특정 파일이 클 경우 업로드가 오래걸릴 수 있기에 가급적 비동기를 사용해서 처리하려고 했다.
const CampaignMediaForm = (props) => {
const [isFileLoading, setIsFileLoading] = useState(false)
const [files, setFiles] = useState<any>([])
const { upload, multiUpload } = useFileUpload()
const multiUploadFile = async (data) => {
setIsFileLoading(true)
const fileData = await multiUpload(data)
handleMultiFiles(fileData)
setIsFileLoading(false)
}
const handleMultiFiles = (fileArr) => {
const fileUrls = JSON.parse(JSON.stringify(files))
const newlySavedFiles = Array.from(fileArr)
const newFiles = fileUrls.concat(newlySavedFiles)
setFiles(newFiles)
}
return (
<div className='offer_file'>
<input
type='file'
name='file'
id='multiple-file'
multiple={true}
onChange={multiUploadFile}
style={{ display: 'none' }}
/>
<div className='h4'>
참고자료 등록
<label
id='fileImg'
htmlFor='multiple-file'
onClick={openFileDialog}>
+
</label>
</div>
</div>
)
}
multiUploadFile
을 통해 next s3 upload에서 반환한 업로드된 파일 배열을 반환받고, handleMultiFiles
를 통해 state에 저장해주었다. (이 state에 저장된 내역을 화면에 뿌려주게 된다)
input의 경우 label을 사용하여 모양을 이쁘게 custom하였다.
custom input을 사용하고 싶을 때는
input에다가display: none
을 걸어두고, label에서 이 input을 인식하도록 연결한다.
연결은for
(리액트에서는htmlFor
)을 사용해서 input에 걸어둔 id를 작성하면 연결된다