-
[react-quill] 커스텀 이미지 핸들러 구현하기Studying/React 2023. 7. 14. 09:20
개요
Quill 에디터는 이미지를 첨부하면 base64 인코딩 형식 변환되어 저장됩니다. 이를 서버에 그대로 전송할 경우에는 자체 string이 너무 길어서 서버에 저장되지 않습니다.
따라서 Quill 에디터를 사용할 때는 자체 커스텀 이미지 핸들러를 만들어 base64 인코딩 방식을 피해 서버에 저장해야 합니다.
이미지 업로드 순서
Quill 에디터에서 기본 제공하는 base64 이미지 업로드 대신, 직접 서버에 이미지를 보내는 방식을 사용해보겠습니다.
이미지가 업로드되는 순서는 다음과 같습니다.
1. 파일 탐색기에서 이미지를 선택한다.
2. 이미지를 받자마자 서버로 보낸다.
3. 서버에서 storage에 이미지를 저장한다.4. 서버에서 이미지 url + id 값을 반환한다.
5. 업로드한 이미지 url을 담는 Array 타입의 상태를 업데이트한다.
6. 받은 이미지 url로 <img> 요소를 생성한다.7. 생성한 <img> 요소를 현재 에디터 커서 위치에 삽입한다.
서버에 이미지 보내기
가장 먼저 서버에 이미지를 POST 하는 API 핸들러를 작성합니다.
const uploadPicture = (body: any) => { return axios({ headers: { 'Content-Type': 'multipart/form-data', accessToken: accessToken, }, method: 'POST', url: API_BASE_URL + '/s3/picture', data: body, }); };
커스텀 이미지 핸들러 만들기
다음은 커스텀 이미지 핸들러입니다. 툴바의 이미지 삽입 버튼을 클릭했을 때, Quill 에디터가 기본 제공하는 이미지 핸들러 대신 아래와 같은 핸들러를 발동시킬 예정입니다.
const imageHandler = useCallback((editor: any) => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.setAttribute('name', 'file'); input.setAttribute("multiple", ""); input.click(); input.onchange = async (event: any) => { const files: FileList = event?.target?.files; const formData = new FormData(); for (let i = 0; i < files.length; i++) { formData.append("file", files[i]); } const response: any = await uploadPicture({ file: files[0] }); response.imageUrl.forEach((url: string) => { dispatch(setPictures(url)); const range = editor.getSelection(); editor.insertEmbed(range.index, "image", url); editor.setSelection(range.index + 1); }) }; }, []);
차근차근 뜯어보면 다음과 같습니다.
먼저 Quill 에디터가 제공하는 이미지 삽입 버튼 대신, 별도의 input 요소를 생성합니다.
const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.setAttribute('name', 'file'); input.setAttribute("multiple", ""); input.click();
해당 input 요소는 이미지 형식의 multiple 파일을 받는 속성을 가집니다.
하지만 이러한 input 요소는 실제 Quill 에디터의 툴바에 존재하는 버튼이 아니기 툴바의 이미지 아이콘을 클릭해도 클릭 이벤트가 발생하지 않습니다.
따라서 HTMLElement.click() 메서드를 사용해 강제로 onClick 이벤트를 발생시킵니다.
해당 input 요소의 onClick 이벤트 핸들러도 바로 아래 작성해주면 됩니다.
input.onchange = async (event: any) => { const files: FileList = event?.target?.files; const formData = new FormData(); for (let i = 0; i < files.length; i++) { formData.append("file", files[i]); } const response: any = await uploadPicture({ file: files[0] }); response.imageUrl.forEach((url: string) => { dispatch(setPictures(url)); const range = editor.getSelection(); editor.insertEmbed(range.index, "image", url); editor.setSelection(range.index + 1); }) };
multiple 속성을 가졌기 때문에 FileList 객체를 가집니다.
FormData 객체를 생성한 뒤, 받은 FileList 안 File 요소들을 하나씩 넣어줍니다.
그리고 맨 처음 작성한 API 함수(uploadPicture)를 통해 서버에 업로드 할 이미지를 보내줍니다.
그 후 서버에서 이미지 url을 반환하면 이를 전역 상태에 저장하고(저는 전역 상태에 formValue 들을 저장했습니다) 현재 마우스 커서에 이미지를 삽입한 다음, 마우스 커서를 한 칸 밀어줍니다.
이렇게 하면 아주 간단하게 Quill 에디터에서 이미지를 업로드하고 서버에 저장할 수 있게 됩니다!
'Studying > React' 카테고리의 다른 글
뒤로가기/새로고침 시 '정말 나가시겠습니까?' 모달 만들기 (0) 2023.07.18 Custom Hook에 ref를 전달하기 (Feat. React-Quill) (0) 2023.07.14 Warning: React does not recognize the ` ` prop on a DOM element (0) 2023.07.12 React quill Editor + react-hook-form feat.TypeScript (0) 2023.07.11 useQuery GET으로 받은 데이터가 props로 전달되지 않을 경우 (0) 2023.07.10