이미지파일 첨부 시 사용자가 파일이 정상적으로 첨부되었다는 사실을 알 수 있도록 화면에 미리보기를 띄우고 삭제할 수 있는 기능을 만들고자 한다.
multiple
속성을 추가한다. <label htmlFor="inputFile">
<div className={styles.chooseFile}>사진 선택</div>
</label>
<input
type="file"
id="inputFile"
accept="image/*"
multiple
className={styles.imageForm}
onChange={handleImageChange}
/>
label
태그의 htmlFor
value와 input
태그 id
의 value를 같은 값으로 맞춘다.display: none
속성을 입히면 된다!createObjectURL
를 통해 만들어진 url는 해당 브라우저가 존재한 상태에서 revokeObjectURL
메소드로 url을 무효화 시키지 않으면 js 엔진에서 garbage collect를 시키지 않고 계속 변수가 남아 메모리 누수가 된다고 하니 주의하자.FileReader
객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는 File 혹은 Blob 객체를 이용해 파일의 내용을 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 한다.이벤트 객체를 매개변수로 전달하여 함수는 선택한 이미지 파일들을 images 배열에 추가하고, 각 이미지 파일에 대한 미리보기 이미지를 previews 배열에 추가한다.
반복문을 사용하여 선택한 이미지 파일의 개수만큼 실행하며, newImages와 newPreviews 배열을 복사한 뒤 이미지 파일의 크기가 3개 이하일 경우에만 새로운 이미지와 미리보기 이미지를 추가한다.
새로운 이미지를 추가하면 FileReader
객체를 사용하여 해당 이미지를 읽어오고, 읽어온 이미지를 base64 형태의 문자열로 변환하여 newPreviews
배열에 추가한다. 그리고 setPreviews
함수를 호출하여 상태값인 previews 배열을 갱신해준다.
readAsDataURL
메서드로 컨텐츠를 특정 Blob 이나 File에서 읽어 올 수 있다.
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newImages = [...images];
const newPreviews = [...previews];
for (let i = 0; i < e.target.files!.length; i++) {
const file = e.target.files![i];
// 이미지 파일 3개로 제한
if (newImages.length < 3) {
// 이벤트객체의 파일을 newImages에 담기
newImages.push(file);
// 파일리더 객체 생성
const reader = new FileReader();
// 파일 읽어온 후 실행되는 콜백함수
reader.onload = (e) => {
// 읽어온 값을 갱신하기
newPreviews.push(e.target!.result as string);
setPreviews(newPreviews);
};
// 파일 객체를 읽어 base64 형태의 문자열로 변환
reader.readAsDataURL(file);
}
}
setImages(newImages);
};
<div className={styles.imgContainer}>
// 미리보기 이미지 순회하여 화면에 띄우기
{previews?.map((preview, index) => (
<div className={styles.imgBox} key={index}>
<Image
src={preview}
width={200}
height={200}
alt={`${preview}-${index}`}
/>
<IoMdClose
className={styles.deleteImg}
onClick={() => handleDeletePreview(index)}
/>
</div>
))}
</div>
splice
메소드를 사용하여 newImages와 newPreviews 배열에서 index에 해당하는 이미지와 미리보기 이미지를 각각 1개씩 삭제한다. const handleDeletePreview = (index: number) => {
const newImages = [...images];
const newPreviews = [...previews];
newImages.splice(index, 1);
newPreviews.splice(index, 1);
setImages(newImages);
setPreviews(newPreviews);
};