지식 정리 📝

이미지 미리보기 & 삭제 & 순서 바꾸 바꾸기

엄성준 2024. 1. 11. 09:09
  const [dragStartIndex, setDragStartIndex] = useState<null | number>(null);
  const [dragEnterIndex, setDragEnterIndex] = useState<null | number>(null);


  const handleAddShowImg = (e: ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return;
    if (e.target.files.length + showImg.length > 8) {
      alert('등록할 수 있는 이미지의 최대 갯수는 8장입니다.');
      return;
    }

    const imgList = e.target.files;
    const imgUrlList = [...showImg];

    for (let i = 0; i < imgList.length; i++) {
      const imgSrc = URL.createObjectURL(imgList[i]);
      const imgName = imgList[i].name;
      imgUrlList.push({ imgSrc, imgName });
    }

    setShowImg(imgUrlList);
  };

  const handleDeleteShowImg = (index: number) => {
    const newShowImgs = showImg.filter((img, i) => i !== index);
    setShowImg(newShowImgs);
  };

  const handleDrag = () => {
    if (dragStartIndex === null || dragEnterIndex === null) return;

    const newShowImgs = [...showImg];
    const spliceImg = newShowImgs.splice(dragStartIndex, 1)[0];

    newShowImgs.splice(dragEnterIndex, 0, spliceImg);
    setShowImg(newShowImgs);
  };

<tr>
              <td className='text-sm font-medium border bg-gray-200 border-gray-300 text-center'>
                Photo
              </td>
              <td className='border border-gray-300 p-2'>
                <div className='flex flex-col gap-1 w-full'>
                  <label
                    htmlFor='photo'
                    className='w-20 border border-gray-400 text-gray-700 hover:bg-gray-50 rounded-md flex justify-center items-center py-0.5 cursor-pointer'
                  >
                    파일 선택
                    <input
                      id='photo'
                      type='file'
                      accept='image/png, image/jpeg'
                      multiple
                      onChange={(e) => handleAddShowImg(e)}
                      className='hidden'
                    />
                  </label>
                  {showImg.length > 0 && (
                    <div className='flex w-full gap-1 py-2 flex-wrap'>
                      {showImg.map((img, i) => {
                        return (
                          <div key={`${img.imgName} + ${i}`}>
                            <img
                              className='w-32 h-32 border border-gray-300 cursor-move'
                              src={img.imgSrc}
                              alt={`${img.imgName} + ${i}`}
                              onDragStart={() => setDragStartIndex(i)}
                              onDragEnter={() => setDragEnterIndex(i)}
                              onDragEnd={() => handleDrag()}
                            />
                            <div className='flex items-center justify-between mt-1'>
                              <span className='truncate max-w-[5rem] text-'>
                                {img.imgName}
                              </span>
                              <button
                                className='border px-1 rounded-md'
                                onClick={() => handleDeleteShowImg(i)}
                              >
                                삭제
                              </button>
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  )}
                  <span className='text-xs text-red-600'>
                    ※ 등록한 사진 순서대로 게시글에 보여집니다.
                  </span>
                  <span className='text-xs text-red-600'>
                    ※ 한 게시물당 최대 5개의 사진을 등록할 수 있습니다.
                  </span>
                </div>
              </td>
</tr>

 

핵심은 이미지마다 onDragStart, onDragEnter Event를 통해서 작업을 했습니다.

 

onDragStart는 마우스로 집은 이미지의 Index를 dragStartIndex state로 관리하였고 집은 이미지가 Enter 즉 올라갔을 때 onDragEnter Event를 통해서  dragEnterIndex state를 관리하였습니다.

 

splice() 함수를 통해서 이미지의 순서를 바꾸는 로직은 onDragEnd Event를 통해서 호출했는데 onDragEnd Event가 호출되면 이미지 이동이 끝났기 때문에 이때 함수를 실행시켰습니다.

 

실행 화면