next.js 이미지 다중 업로드 미리보기+삭제 기능 구현

moreas·2023년 4월 25일
2

팀 프로젝트

목록 보기
10/14
post-thumbnail

어떤 글을 쓸 거냐면

이미지파일 첨부 시 사용자가 파일이 정상적으로 첨부되었다는 사실을 알 수 있도록 화면에 미리보기를 띄우고 삭제할 수 있는 기능을 만들고자 한다.


☁️ 이미지 첨부 파일 다중 업로드

input 태그 사용

  • 파일 업로드로 이미지 다중 업로드를 하려면 multiple 속성을 추가한다.
  • 이미지 파일이라면 형식 상관없이 추가할 수 있도록 "image/*" 속성을 추가해주었다.
<label htmlFor="inputFile">
  <div className={styles.chooseFile}>사진 선택</div>
</label>
 <input
   type="file"
   id="inputFile"
   accept="image/*" 
   multiple
   className={styles.imageForm}
   onChange={handleImageChange}
/>

input 태그 css 커스텀하기

  • 기존 input의 디자인을 사용하지 않으려면 label태그의 htmlFor value와 input태그 id의 value를 같은 값으로 맞춘다.
  • label에 css를 입혀 사용하면 클릭했을 때 파일 첨부가 가능하다. 나는 label 내부에 div 사용해서 스타일을 주었다. input 태그는 display: none 속성을 입히면 된다!

🌈 이미지 미리보기

  • 인풋 태그에 업로드한 이미지를 화면에 보여주는 기능을 추가한다.

이미지 미리보기

  • 이미지를 미리보기로 구현하는 방법은 URL을 사용하는 방법과 FileReader를 사용하는 방법이 있다.

1) URL.createObjectUrl 메소드 사용

  • 쉽게 말해 이미지의 경로를 상대경로로 사용하는 것이다.
  • createObjectURL를 통해 만들어진 url는 해당 브라우저가 존재한 상태에서 revokeObjectURL 메소드로 url을 무효화 시키지 않으면 js 엔진에서 garbage collect를 시키지 않고 계속 변수가 남아 메모리 누수가 된다고 하니 주의하자.

2) FileReader API 사용

  • FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는 File 혹은 Blob 객체를 이용해 파일의 내용을 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 한다.
  • 여기서는 FileReader를 사용하여 미리보기를 구현할 것이다.

FileReader API

  • 이벤트 객체를 매개변수로 전달하여 함수는 선택한 이미지 파일들을 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);
  };

미리보기 이미지 보여주기

  • 미리보기 이미지 순회하여 preview 를 Image 컴포넌트의 src로 지정해준다.
   <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개씩 삭제한다.
  • 마지막으로 setImages와 setPreviews 함수에 새 배열로 갱신해서 저장한다.
  const handleDeletePreview = (index: number) => {
    const newImages = [...images];
    const newPreviews = [...previews];
    newImages.splice(index, 1);
    newPreviews.splice(index, 1);
    setImages(newImages);
    setPreviews(newPreviews);
  };

  • close 버튼 onClick 이벤트에 함수를 적용하여 버튼을 클릭하면 이미지가 삭제된다.
profile
Everything is connected 🐶 좀 더 나은 개발을 위해

0개의 댓글