기존에는 이미지 하나만 올려서 처리하는 방식이였지만, 여러개 다중 파일을 올리는 기능을 추가로 구현했다. 그리고 부가적인 기능도 추가로 구현한 내용을 정리했다.
https://velog.io/@jkl1545/이미지-다중-업로드-및-미리보기
https://ramincoding.tistory.com/entry/ReactProject-input-파일-이미지-업로드-미리보기Preview-기능-구현다중-이미지
https://ramincoding.tistory.com/entry/ReactProject-input-으로-업로드한-여러-개의-이미지-하나씩-삭제하기
const [inputFileList, setInputFileList] = useState([]); // 입력한 파일 리스트 정보 저장
// 이미지 입력시 실행되는 함수
const onImageChange = useCallback(
(e) => {
const fileList = e.target.files; // 입력된 파일 형태의 데이터
if (fileList.length > 0) {
let imageUrlList = [...inputFileList];
// 최대 7개 파일까지만
if (fileList.length > 7) {
fileList = fileList.slice(0, 7);
}
// 입력된 파일 모두 url 생성 및 정보 저장
for (let i = 0; i < fileList.length; i++) {
// 이미지 미리보기 기능을 위해서 브라우저상에서 url을 생성함
const curImageUrl = URL.createObjectURL(fileList[i]);
// 중복 파일 검사
const isDuplicate = imageUrlList.some(
(file) => file.name === fileList[i].name
);
// 중복이 아닌 경우에만 추가
// 생성된 url이랑 입력한 이미지 정보 저장
if (!isDuplicate) {
imageUrlList.push({
name: fileList[i].name,
url: curImageUrl,
type: fileList[i].type,
file: fileList[i],
});
}
}
setInputFileList(imageUrlList);
}
},
[inputFileList]
);
...
{/* 이미지를 사용자로부터 받기 위한 input (hidden) -> button */}
<button
className="w-2/6 h-12 px-4 py-2 bg-sub-blue text-white text-center text-sm rounded cursor-pointer transition duration-300 hover:bg-main-blue focus:outline-none"
onClick={handleFileInput}
>
파일 선택
</button>
<input
className="hidden"
type="file"
accept="image/jpg, image/jpeg, image/png, .pdf, .DWG"
ref={fileInputRef}
onChange={onImageChange}
id="fileInput"
multiple
/>
미리보기 default 값 설정
// 이미지 미리보기 값 설정
useEffect(() => {
// default 값 설정
if (inputFileList.length > 0) {
// 특정 파일을 선택하지 않은 경우 맨 첫번째 위치한 파일이 미리보기로 보여짐
if (!selectFile) {
const file = inputFileList[0];
setSelectFile(file.url);
setImagePreview(file.url);
setSendIndex(0);
// 이미지 파일, pdf 파일 타입 설정
const type = file.type;
if (type.includes("image")) {
setFileType("image");
} else if (type.includes("pdf")) {
setFileType("pdf");
}
}
}
}, [inputFileList]);
imagePreview
, selectFile
, sendIndex
값을 설정한다. // 선택한 이미지 Preview 설정
const selectPreviewImage = useCallback(
(image, index) => {
setImagePreview(image.url);
setSelectFile(image.url);
setSendIndex(index);
// 이미지 파일, pdf 파일 타입 설정
if (image.type.includes("image")) {
setFileType("image");
} else if (image.type.includes("pdf")) {
setFileType("pdf");
}
},
[inputFileList]
);
....
{/* 업로드 파일 리스트 */}
<div className="flex-row-center w-2/6 h-full p-4">
<div className="w-full max-h-full px-2 overflow-auto">
{inputFileList &&
inputFileList.map((v, i) => (
<div
key={i}
className="flex justify-between items-center w-full px-4 py-2 mb-2 text-sm border border-border-gray rounded-lg"
>
<span
className={`whitespace-nowrap mr-4 font-normal cursor-pointer ${
selectFile === v.url
? "text-sub-blue font-semibold"
: ""
}`}
onClick={() => {
selectPreviewImage(v, i);
}}
>
{v.name}
</span>
<span
className="whitespace-nowrap font-bold text-gray-600 hover:text-main-red"
onClick={() => removeImg(v.url, i)}
>
<GoTrash />
</span>
</div>
))}
</div>
</div>
selectFile
정보 삭제inputFileList
에서 선택된 이미지를 제외한 배열로 새롭게 설정revokeObjectURL
메서드를 통해서 메모리에서 삭제해준다.// 삭제된 이미지 파일은 url 해제
const removeUrl = (url) => {
return () => URL.revokeObjectURL(url);
};
// 업로드 한 이미지 삭제 함수
const removeImg = useCallback(
(url, index) => {
if (selectFile === url) {
setSelectFile(null);
}
// 삭제 선택된 index 배열에서 제외
const updatedFileList = inputFileList.filter((_, i) => i !== index);
setInputFileList(updatedFileList);
// 삭제된 이미지 URL값 메모리에서 삭제
removeUrl(url);
},
[inputFileList, selectFile]
);