[ react ] 게시글작성 페이지 - 이미지 업로드 (여러개사진)

Suji Kang·2023년 11월 18일
0
post-thumbnail

label 인데 속에 img 가 들어있는 label

두번째 label은 항상 추가되는 label

const ActivityCU = () => {
    const [imgList, setImgList] = useState([]);
  
    return (
<ImgInputWrap>
{
    imgList.map((img) =>
        <label>
            <img
                style={{
                    width: '100%',
                    height: '100%',
                    objectFit: 'cover',
                    objectPosition: 'center'
                }}
                src={imgList} />
            <input onChange={onImgSelected} accept="image/*" type="file" />
        </label>
    )
}
		<label>
    		+
    		<input onChange={onImgSelected} accept="image/*" type="file" />
		</label>
</ImgInputWrap>
);

이제 사진을 추가하면 바로옆에 플러스가 생겨서 계속 추가 가능❗️❗️


그런데, 만약 인절미 사진을 다른걸로 바꾸려면(수정) ❓ 또는 삭제하려면 ❓

Delete (삭제하기)

type='button'
submit 하면 새로고침되니까.

  • 배열에서 클릭한애를 지워줘 라고해야한다.
{
    imgList.map((img) =>
        <label>
            <img
                style={{
                    width: '100%',
                    height: '100%',
                    objectFit: 'cover',
                    objectPosition: 'center'
                }}
                src={imgList} />
            {/* <input onChange={onImgSelected} accept="image/*" type="file" /> */}
            <button type='button'>삭제</button>
        </label>
    )
}

Edit (수정하기)

우선 ❗️우리가 올린이미지가 몇번 이미지인지 각각 구분할수있어야한다.
처음에는
imgList는 빈 배열이였는데 imgList : [] , 이미지를 클릭하면 이렇게 id, previewUrl, origin 객체가 들어간다.

🌟 setImgList(
[...imgList, 
	{ id, 
	previewUrl: reader.result, 
	originFile: e.target.files[0] }
]); 
<const ActivityCU = () => {
    const [imgList, setImgList] = useState([]);

    const onImgSelected = (e) => {
        //이미 이미지가 업로드 되었던것을 다시 선택해서 실행될때는
        //기존의 배열에서 기존의 선택된 이미지를 지우고 
        
        //새로운 선택한 이미지를 배열에 추가해준다. 
        let now = new Date();
        let id = now.toString(); // -> '2021-09-09T10:00:00'
       
        // FileReader(); // 내가 선택한 파일을 읽어주는 객체
        let reader = new FileReader(); //객체생성함
        reader.readAsDataURL(e.target.files[0]); //내가 선택한 파일을 읽어주는 함수 
        // readAsDataURL -> 비동기함수임 
        //readAsDataURL()함수는 내가 선택한 파일을 읽어서 url로 만들어준다.
        //이미지가 url로 만들어진 후에 실행되는 함수

        reader.onload = () => { //이미지가 url로 만들어진 후에 실행되는 함수
            console.log(reader.result); //이미지가 url로 만들어진다.
            setImgList([...imgList, { id, previewUrl: reader.result, originFile: e.target.files[0] }]);  
        }
    }

아이디를 날짜로 가져와서 설정할수있음(겹치지않으니까).

이렇게 원래있던것을 새로운것으로 바꾸는것.(덮어씌우기)

아니면, 있던것 삭제하고 추가해도됨 (이것의 안좋은점은, 순서가 바뀜)

근데 우리는 순서까지 그대로인것을 사용해보자❗️

기존의 배열에서 id 가 동일한 요소 찾고, 같은 id를 찾으면,

  const onImgChanged = (id) => {
        //이미 이미지가 업로드 되었던것을 다시 선택해서 실행될때는
        //기존의 배열에서 기존의 선택된 이미지를 지우고 
        //🌟 기존의 배열에서 id 가  동일한 요소 찾기
        //기존 imgList 라는 state변수(배열)은 변하면 안되기 때문에 똑같은 요소를 갖고있는 복제본 생성
        
        let target = imgList.find((el) => {
            return el.id === id;
        })
        target.previewUrl = null;
    }
  
    return(
   		 <input
   			 onChange={() => onImgChanged(img.id)}
   			 accept="image/*"
   			 type="file" />
    );

선택한 요소 id 와 imgList에 있는 id가 같은것을 find()함수로 찾아낸다.

원본을 훼손할 가능성이 있기 때문에, - state변수는 원본을 수정하면 안된다.
target.previewUrl이 바뀌면 imgurl도 같이 바뀐다. 그러면 바뀌기 전하고 후가 구분이 안된다.

🔎 깊은복사를 해줘야한다.

let cpy = [...imgList];
//배열 복제 //새로운배열 생성. 객체가 들어있다.
여기는 문제가 생긴다. int, string 타입이 아닌 객체가 들어있기 때문에 사용할수없음. 하지만 int, string 타입이라면 사용가능.

📌 JSON.stringify() - 문자열로 바꿔주는 함수

JSON.stringify(imgList) // 문자열로 바꿔주는 함수

📌 JSON.parse() - 배열로 바꿔주는 함수

JSON.parse(JSON.stringify(imgList))

👉👉👉 이렇게 하면 완전히 복사가됨.

cpy에 객체가 들어가있음.(원본훼손없이)


배열안에 객체가 들어가있음.

cpy의 0번째방에 ff0객체가 들어있고
cpy의 1번째방에 fb8객체가 들어있고
cpy의 2번째방에 faa객체가 들어있고

👉 복제본이 되어 서로 내용은 똑같다 (imgurl = cpy). 하지만, 완전 다른것이다.(이렇게 원본은 훼손하지않고 cpy를 수정할수있다.)

 const onImgChanged = (id, e) => {
        //이미 이미지가 업로드 되었던것을 다시 선택해서 실행될때는
        //기존의 배열에서 기존의 선택된 이미지를 지우고 
        //기존의 배열에서 id 가  동일한 요소 찾기
        //기존 imgList 라는 state변수(배열)은 변하면 안되기 때문에 똑같은 요소를 갖고있는 복제본 생성
        // let cpy = [...imgList];  //배열 복제 //새로운배열 생성. 객체가 들어있다. 
        //여기는 문제가 생긴다. int, string 타입이 아닌 객체가 들어있기 때문에 사용할수없음. 하지만 int, string 타입이라면 사용가능.  
        let cpy = JSON.parse(JSON.stringify(imgList)); //문자열로 바꾸고 다시 객체로 바꾸면 복제본이 생성된다.

        let target = cpy.find((e) => { //복제본을 넣어준다.
            return e.id === id;
        }); //복제본을 넣고, find 함수로 id가 같은것을 찾으면 taget이라는 이름으로 . 

        const reader = new FileReader();
        reader.readAsDataURL(e.target.files[0]); //미리보기 url. 어떤url을 미리보기할건지()안에 넣어줘야한다. 그래서, e.target.files[0] --> 사용자가 업로드한 이미지 파일
        reader.onload = () => { //다 읽어지면(완료가되면) 실행되는 함수
            target.previewUrl = reader.result; //previewUrl -> 미리보기 바꾸고,
            target.originFile = e.target.files[0]; //origin -> 원본파일도 바꾸고
            setImgList(cpy); //setImgList에서, cpy원본으로 바꿔줘
        }
    }
return (
    {
        imgList.map((img) =>
            <label style={{ position: 'relative' }} key={img.id}>
                <img
                    style={{
                        width: '100%',
                        height: '100%',
                        objectFit: 'cover',
                        objectPosition: 'center'
                    }}
                    src={img.previewUrl} />
                <input
                    onChange={(e) => onImgChanged(img.id, e)} //받아온 id랑 ,지금받아온 event를 받아옴
                    accept="image/*"
                    type="file" />
            </label>
        )
    }
)

🔎 이미지 삭제하기

const onImgDelete = (id) => {
        setImgList(imgList.filter((e) => e.id !== id));
    }

return(
<button
    style={{ position: 'absolute', top: '0', right: '0' }}
    onClick={() => { onImgDelete(img.id) }}
    type='button'>삭제하기
</button>
  )
profile
나를위한 노트필기 📒🔎📝

0개의 댓글