React Quill에서 이미지를 업로드하기 위한 올바른 방법


1. 대부분의 React Quill 사용법
사내에서 상품 판매 form을 만들어야 해서 React Quill 에디터를 사용한 적이 있다.
기본적으로 React Quill 에디터에서 이미지를 업로드하면 Base64로 업로드되기 때문에 서버에 이미지를 업로드하고 url을 받는 방법을 찾기 위해 구글링을 해보았다.
거짓말 안 보태고 모든 아티클에서 imageHandler를 이렇게 사용하라고 알려주고 있었다.
javascriptexport default function Editor({ value, onChange, ...other }) { const quillRef = useRef(null); const imageHandler = () => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.addEventListener('change', () => { const files = input.files; // image upload logic }); }; const modules = useMemo(() => { return { toolbar: { container: [["link", "image"]], handlers: { image: imageHandler, }, }, history: {}, clipboard: {}, // custom }; }, []); return ( <ReactQuill ref={quillRef} theme="snow" modules={modules} formats={formats} value={value} onChange={onChange} placeholder="input text..." {...other} /> );}
어차피 DOM에 첨부를 위한 임시 input을 쓰는 거니깐 큰 문제는 없을 거라 생각해서
나도 이런 방식으로 에디터 컴포넌트를 만들었다.
2. 버그 발생
이미지를 aws s3에 presigned url 방식으로 관리하고 있었는데
bucket key에 상품명과 상품 코드를 넣어달라는 요청이 있었다.
그러기 위해서는 상품명, 코드가 없으면 이미지 업로드가 불가능한 상태..
그래서
typescriptconst uploadFilesAndGetUrls = (files) => { if (!productName || !productCode) { alertToast("상품명과 상품코드를 입력해야 사진 첨부가 가능합니다."); return null; } // ... s3 이미지 업로드 로직
이런식으로 했고, 상품명과 같은 입력 데이터는 react-hook-form으로 관리했다.
그래서 Editor 컴포넌트에
tsx// ... export default function Editor({ value, onChange, uploadFilesAndGetUrls, ...other}) { const quillRef = React.useRef(null); const imageHandler = () => { const input = document.createElement("input"); input.type = "file"; input.multiple = true; input.accept = "image/*"; input.click(); input.addEventListener("change", () => { const files = input.files; const imgUrls = uploadFilesAndGetUrls(files); if (!imgUrls) return; imgUrls.forEach((file) => insertImage(file, quillRef)); }); }; // ...
이렇게 uploadFilesAndGetUrls
함수를 prop으로 넣어주었고 테스트를 해보았다.
그런데 자꾸만 이 부분에서 막혔다.
javascriptalertToast("상품명과 상품코드를 입력해야 사진 첨부가 가능합니다.");
- 처음 상품을 등록할 때 form을 다 입력해도 막힘
- 새로고침 후 이미지를 업로드하면 또 안 막힘
이미지 처리 로직 자체도 상당히 길었어서 여기서 한참을 헤매게 됩니다.
3. 해결
여러 테스트를 해봤는데 첫 로드 이후 입력하는 값들이 함수에 반영이 안 되고 있었음.
그 말은 동적으로 바뀌는 값들을 반영하지 못 한다는 건데..
imageHandler
함수를 들여다보면
javascriptconst imageHandler = () => { const input = document.createElement("input"); input.type = "file"; input.multiple = true; input.accept = "image/*"; input.click(); input.addEventListener("change", () => { const files = input.files; const imgUrls = uploadFilesAndGetUrls(files); if (!imgUrls) return; imgUrls.forEach((file) => insertImage(file, quillRef)); }); };
ReactDom이 아니라 DOM에 생성한 input에 uploadFilesAndGetUrls
함수를 넣으니깐
state가 변경돼도 반영이 안 되는 것이었음.
구글링으로 참고한 포스트들을 원망하며 ReactDom 내에서 이미지 업로드를 해보기로 합니다.
jsx// ... export default function Editor({ value, onChange, uploadFilesAndGetUrls, ...other}) { const quillRef = React.useRef(null); // 이미지를 업로드하기 위한 input ref const inputRef = React.useRef(null); const onImageUpload = () => { const files = inputRef.current.files; const imgUrls = uploadFilesAndGetUrls(files); if (!imgUrls) return; imgUrls.forEach((file) => insertImage(file, quillRef)); }; const modules = React.useMemo(() => { return { toolbar: { container: [["link", "image"]], handlers: { // input trigger image: () => inputRef.current.click(), }, }, }; }, []); return ( <> <ReactQuill {/* ... */} /> {/* 이미지를 업로드하기 위한 input */} <input ref={inputRef} type="file" accept="image/*" className="hidden" onChange={onImageUpload} multiple /> </> );}
해결했습니다.
4. 회고
ReactDom에서 DOM API를 사용했을 때 일어나는 버그를 몸소 느껴봤네요
정말 많은 사람들이 React Quill을 이렇게 쓰고 있는데 하필 나에게 이런 일이
그래서 React Quill 깃헙 이슈에 올려 공유했습니다.
https://github.com/zenoamaro/react-quill/issues/961Related Articles
- 모바일 키보드를 고려한 버튼 위치 맞추기입력창에 맞춰 키보드가 올라오면 버튼이 가려지는 경우 버튼을 키보드에 맞춰 올린 경험이에요2024.08.08ryxxn
- React 모달 라이브러리를 만들기까지의 변천사저는 간단한 모달 컴포넌트조차 매번 넣어야 하는 게 불편했어요. 그래서 모달 컴포넌트를 함수로 간단히 실행하는 발상에서 시작해, react-handle-alert라는 라이브러리를 탄생시키기까지의 여정을 담았습니다.2024.05.04ryxxn
- Firebase와 Algolia 동기화 지연 문제 해결하기firebase database와 algolia 간의 동기화 사이에 data fetching했을 때 싱크가 맞지 않는 문제를 찾고 해결한 경험이에요2024.03.24ryxxn
- 설악버스 개발기정류장에 부착돼있는 버스 시간표가 어르신들과 아이들이 보기 힘들어하는 모습을 많이 봤습니다. 그렇게 만들게 된 버스 시간표 웹앱 개발기입니다.2024.03.22ryxxn