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가 호출되면 이미지 이동이 끝났기 때문에 이때 함수를 실행시켰습니다.

'지식 정리 📝' 카테고리의 다른 글
Mui DatePicker 키보드 Event PreventDefault 하기 (0) | 2024.01.12 |
---|---|
Prisma CLI 모음 (0) | 2024.01.12 |
PlanetScale DB 연결 법 (0) | 2024.01.10 |
PlanetScale CLI (zsh: command not found: brew Error 해결법) (0) | 2024.01.09 |
세상에서 가장 간단한 Skeleton UI With Only HTML & CSS (2) | 2024.01.04 |