//////
Search
🔧

이미지 미리보기 구현

작성자
날짜
2023/01/02 15:27
카테고리
ETC
Today’s Trouble post upload 페이지 내 이미지 첨부파일 미리보기를 구현하는 와중에 이미지 삭제 버튼을 누르면 submit이 발생하여(아마도?) 페이지가 새로고침되어 이미지 뿐만 아니라 글 내용도 사라지는 현상 발생

문제해결

1. 문제가 발생한 코드

export function PostUploadForm() { // 중략 const handleFileUpload = (e) => { setImgFile(URL.createObjectURL(e.target.files[0])); }; const handleFileDelete = () => { URL.revokeObjectURL(imgFile); setImgFile(''); }; return ( <> <HeaderUpload isDisabled={isDisabled} /> <S.Container> <h2 className='sr-only'>게시글 작성</h2> <S.ProfileImg /> <S.PostWrite> <h3 className='sr-only'>게시글 작성 form</h3> <S.Form> <S.ContentInput onInput={handleTextArea} ref={textRef} /> <S.ImgUploadButton onChange={handleFileUpload}> <h4 className='sr-only'>이미지 업로드 버튼</h4> <MediumImgButton /> </S.ImgUploadButton> {imgFile && <PhotoUploadList imgFile={imgFile} setImgFile={setImgFile} handleFileDelete={handleFileDelete}/>} </S.Form> </S.PostWrite> </S.Container> </> ); }
JavaScript
복사
PostUploadForm.jsx
export function PhotoUploadList({ imgFile, handleFileDelete }) { return ( <S.PhotoUploadList> <Photo imgFile={imgFile} setImgFile={setImgFile} handleFileDelete={handleFileDelete}/> </S.PhotoUploadList> ); }
JavaScript
복사
PhotoUploadList.jsx
export function Photo({ imgFile, handleFileDelete }) { const url = imgFile; return ( <S.Photo> <img src={url} alt='' /> <S.PhotoRemove type='button' onClick={handleFileDelete} /> </S.Photo> ); }
JavaScript
복사
Photo.jsx
이와 같이 부모 요소에 이벤트함수를 작성하고 자식 요소들에게 props로 전달하였더니, button의 type이 button임에도 불구하고 form의 자식요소이기 때문인지, 이벤트 플로우 때문에 이벤트 캡처링과 버블링이 일어나서인지 자꾸 새로고침이 되는 현상이 발생하였다.  아마도 둘 다

2. 해결 방법

export function PostUploadForm() { // 중략 const handleFileUpload = (e) => { setImgFile(URL.createObjectURL(e.target.files[0])); }; // 중략 return ( <> <HeaderUpload isDisabled={isDisabled} /> <S.Container> <h2 className='sr-only'>게시글 작성</h2> <S.ProfileImg /> <S.PostWrite> <h3 className='sr-only'>게시글 작성 form</h3> <S.Form> <S.ContentInput onInput={handleTextArea} ref={textRef} /> <S.ImgUploadButton onChange={handleFileUpload}> <h4 className='sr-only'>이미지 업로드 버튼</h4> <MediumImgButton /> </S.ImgUploadButton> {imgFile && <PhotoUploadList imgFile={imgFile} setImgFile={setImgFile} />} </S.Form> </S.PostWrite> </S.Container> </> ); }
JavaScript
복사
PostUploadForm.jsx
export function PhotoUploadList({ imgFile, setImgFile }) { return ( <S.PhotoUploadList> <Photo imgFile={imgFile} setImgFile={setImgFile} /> </S.PhotoUploadList> ); }
JavaScript
복사
PhotoUploadList.jsx
export function Photo({ imgFile, setImgFile }) { const url = imgFile; const handleFileDelete = () => { URL.revokeObjectURL(imgFile); setImgFile(''); }; return ( <S.Photo> <img src={url} alt='' /> <S.PhotoRemove type='button' onClick={handleFileDelete} /> </S.Photo> ); }
JavaScript
복사
Photo.jsx
이와같이 이벤트가 실질적으로 등록되어야 되는 컴포넌트가 있는 컴포넌트에 이벤트 함수(handleFileDelete)를 작성해 주고, 상태를 관리해 주는 함수(setImgFile)을 props로 전달해서 사용해 주었더니 문제없이 잘 작동하였다.

3. 고민

문제는 해결했는데 props를 이렇게까지 내려받아와도 되는 것인지 약간 고민이 된다.
뎁스가 3~5 사이 정도는 괜찮다는 말들도 있던데 뭔가 정리하고 싶은 욕구가 계속.. 드는 중
나중에 방법을 찾아서 리팩토링을 해봐야 할 것 같다.

New

이 기능을 구현하면서 새롭게 알게된 것들

1. URL.createObjectURL() / URL.revokeObjectURL()

URL.createObjectURL()
주어진 객체를 가리키는 URL을 DOMString으로 반환
해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화
이 명령어를 통해 호출되는 url은 blob 객체 형태로 호출됨
→ 이 url은 다른 window에서 접근이 되지 않음
URL.revokeObjectURL()
객체 URL 해제
메모리 누수의 가능성이 있으므로 revokeObjecURL을 통해 해제해 주는 것을 권장

참고