이미지 다중 업로드 (미리보기, 선택, 삭제)

CHAENG·2024년 7월 25일
0

FrontEnd

목록 보기
9/10

기존에는 이미지 하나만 올려서 처리하는 방식이였지만, 여러개 다중 파일을 올리는 기능을 추가로 구현했다. 그리고 부가적인 기능도 추가로 구현한 내용을 정리했다.

  1. 다중 여러개 올린거 보여지는 파일 리스트 만들기
  2. 파일 리스트 선택하면 해당 이미지를 미리보기로 보여주기
    1. 특정 이벤트가 없거나 파일이 애초에 하나인 경우에는 맨 첫번째 위치하는 파일 보여주기
  3. 파일리스트에서 특정 파일 삭제할 수 있도록 설정하기

https://velog.io/@jkl1545/이미지-다중-업로드-및-미리보기

https://ramincoding.tistory.com/entry/ReactProject-input-파일-이미지-업로드-미리보기Preview-기능-구현다중-이미지

https://ramincoding.tistory.com/entry/ReactProject-input-으로-업로드한-여러-개의-이미지-하나씩-삭제하기


✅ 이미지 여러개 업로드하기

  • 입력 데이터를 저장하는 inputFileList 변수를 사용
  • 입력된 파일 리스트 데이터는 객체형태로 반환됨. (배열 X)
    • fileList는 기본적으로 length라는 속성을 가져서 활용할 수 있음. 배열의 형태는 아님
  • 입력할 수 있는 이미지의 최대 개수는 7개
    • 7개가 넘어갈 경우 slice 문법으로 7개까지만 저장되게 처리함
  • 입력된 파일의 정보 저장
    • name, url, type.. 등 필요한 정보들만 object 형태로 저장
  • 중복된 파일이 존재 할 경우에는 추가로 업로드 하지 않음
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 값을 설정한다.
    • imagePreview : 미리보기 할 이미지의 url
    • selectFile : 선택한 파일의 정보
    • sendIndex : 서버로 보낼 때 사용하기 위한 파일 리스트 index 값
  • map 함수를 활용하여 배열을 순회하며 리스트를 렌더링 함
 // 선택한 이미지 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]
  );
profile
FrontEnd Developer.

0개의 댓글

관련 채용 정보