🔔 [관련 정리 리스트]
- React Quill 1-에디터 사용
- React Quill 2-이미지처리(편집중/참고만)
- React Quill 3-이미지처리(편집완료/사용)
- React Quill 4-이미지 리사이즈
위 목록 중 2와 같이 에디터 편집 중일때 이미지 처리를 하면, 이미지를 선택함과 동시에 서버로 보내고, S3 URL을 얻는다.
이렇게 되면 이미지 업로드 할때마다 서버도 업로드가 되기 때문에, 편집 중에 이미지를 지우거나 편집 완료를 하지 않아도 이미지가 서버에 등록되어 불필요한 용량을 차지하게 된다.
따라서 에디터를 모두 작성한 후 편집 '완료' 버튼을 눌렀을 때 서버에 이미지를 보내고 얻은 URL을 에디터 코드로 저장하고자 한다.
위 주소의 코드를 참고하여 아래 코드로 진행하였다. (작성페이지)
const srcArray = []; // src만 추출
const blopArray = [];// blopArray로 변환
const urlArray = [];// 최종 src url 저장할곳
const gainSource = /(<img[^>]*src\s*=\s*[\"']?([^>\"']+)[\"']?[^>]*>)/g;
let endContent = content;
let index = 0; // 인덱스 변수 추가
while (gainSource.test(content)) {
// console.log('이미지가 있을때만 진행함.');
let result = RegExp.$2;
// console.log('src 추출 결과 : ',result)
srcArray.push(result);
//console.log('srcArray 추가: ', srcArray);
// base64파일 Blop으로 바꾸기
// // dataURL 값이 data:image/jpeg:base64,~~~~~~~ 이므로 ','를 기점으로 잘라서 ~~~~~인 부분만 다시 인코딩
const byteString = atob(result.split(',')[1]);
// Blob를 구성하기 위한 준비
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ia], {
type: 'image/jpeg'
});
// const file = new File([blob], 'image.jpg');
const file = new File([blob], `image-${index}.jpg`);
//console.log('file: ', file); //선택한 이미지
// 위 과정을 통해 만든 image폼을 FormData에 넣어줍니다.
// 서버에서는 이미지를 받을 때, FormData가 아니면 받지 않도록 세팅해야합니다.
const formData = new FormData();
// formData.append('file', file);
formData.append('files[]', file);
index++;
console.log('formData : ', formData);
// 백엔드로 보내서 urlArray에 돌려받은 url을 배열 형태로 push 해준다.
// 최상단 while문이 모든 사진을 추출해 하나씩 저장하여 push하므로
// 백엔드의 multer 패키지에 single로 저장을 요청한다.
const config = {
header: { 'content-type': 'multipart/form-data' }
};
// TODO FormData 백엔드로 넘겨서 url 건네받아 저장하고, url 반환해서 urlArray에 저장하기
/* await axios.post('api/board/uploadImgFolder', formData, config).then((response) => {
if (response.data.success) {
console.log('이미지 서버에 업로드 성공', response);
urlArray.push(response.data.url);
console.log('urlArray에 추가', urlArray);
} else {
console.log(response);
alert('이미지를 서버에 업로드하는데에 실패했습니다.');
}
}); */
urlArray.push(
'https://img1.daumcdn.net/thumb/R1280x0.fjpg/?fname=http://t1.daumcdn.net/brunch/service/user/cnoC/image/66LHXQZY2bFLKR7SEO_KJjOnX6M',
'https://cdn.playforum.net/news/photo/202210/202392_18836_2310.jpg'
);//api 대신 더미로 테스트
console.log('urlArray에 추가', urlArray);
// FormData의 key 확인
/* for (let key of formData.keys()) {
console.log('key', key);
}
// FormData의 value 확인
for (let value of formData.values()) {
console.log('value', value);
} */
// console.log('서버 주소 저장된 어레이: ', urlArray);
//console.log('srcArray: ', srcArray);
// 만일 이미지를 업로드 했다면, 첫번쨰 srcArray가 있는 부분을 첫번째 url로 바꾸는 식으로 계속 바꿔라.
if (srcArray.length > 0) {
/* console.log('실행은 됐음..'); */
for (let i = 0; i < srcArray.length; i++) {
console.log('실행중.. ' + i + ' 번째임');
console.log('srcArray[i]: ', srcArray[i], 'urlArray[i]: ', urlArray[i]);
let replace = endContent.replace(srcArray[i], urlArray[i]);
endContent = replace;
}
} // 없다면 content=content
console.log('endContent:', endContent);
//
/* console.log('최종 urlArray', urlArray);
console.log('최종 srcArray: ', srcArray);
console.log('최종 blopArray: ', blopArray); */
}
const totalValue = { ...values, content: endContent };
console.log(totalValue, 'totalValue---');
작성페이지에서만 이렇게 작성할 경우 다른 페이지에서도 활용하기엔 어려워서 아래와 같이 커스텀Hook으로 분리했다.
1. 커스텀 Hook
(API가 아직 없는 관계로 더미 Data로 적용한 상태)
import { useState } from 'react';
import axios from 'axios';
export default function useQuillImageReplacement() {
const [urlArray, setUrlArray] = useState<string[]>([]);//서버에서 받는 s3 url 저장할곳
const replaceImages = async (content: string) => {
const srcArray: string[] = [];//에디터 이미지들에서 src만 추출
const gainSource = /(<img[^>]*src\s*=\s*[\"']?([^>\"']+)[\"']?[^>]*>)/g;// blopArray로 변환
let endContent = content;
// 이미지 src 추출
while (gainSource.test(content)) {
// console.log('이미지가 있을때만 진행함.');
const result = RegExp.$2;
// console.log('src 추출 결과 : ',result)
srcArray.push(result);
}
// base64파일 Blop으로 바꾸기
// 이미지 업로드 후 URL 치환
for (let i = 0; i < srcArray.length; i++) {
const result = srcArray[i];
// dataURL 값이 data:image/jpeg:base64,~~~~~~~ 이므로 ','를 기점으로 잘라서 ~~~~~인 부분만 다시 인코딩
const byteString = atob(result.split(',')[1]);
// Blob를 구성하기 위한 준비
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let j = 0; j < byteString.length; j++) {
ia[j] = byteString.charCodeAt(j);
}
const blob = new Blob([ia], { type: 'image/jpeg' });
const file = new File([blob], `image-${i}.jpg`);
//console.log('file: ', file); //선택한 이미지
// 위 과정을 통해 만든 image폼을 FormData에 넣어줍니다.
// 서버에서는 이미지를 받을 때, FormData가 아니면 받지 않도록 세팅해야합니다.
const formData = new FormData();
formData.append('files[]', file); //file들이 더해진 전체formData(서버 파일 전송용)
try {
// 실제 API 호출 부분 (여기서 백엔드와 통신)
const config = { header: { 'content-type': 'multipart/form-data' } };
//TODO: FormData 백엔드로 넘겨서 url 건네받아 저장하고, url 반환해서 urlArray에 저장하기
/* const response = await axios.post('/api/board/uploadImgFolder', formData, config);
if (response.data.success) {
setUrlArray(response.data.url);
endContent = endContent.replace(srcArray[i], response.data.url[i]);
} */
// 더미 데이터 (테스트용)
const dummyUrl = [ 'https://img1.daumcdn.net/thumb/R1280x0.fjpg/?fname=http://t1.daumcdn.net/brunch/service/user/cnoC/image/66LHXQZY2bFLKR7SEO_KJjOnX6M',
'https://cdn.playforum.net/news/photo/202210/202392_18836_2310.jpg'];
setUrlArray(dummyUrl);//TODO: 서버에서 받는 S3 url로 추후 변경
//console.log('urlArray', urlArray);
endContent = endContent.replace(srcArray[i], dummyUrl[i]); //이미지 부분 코드 치환(에디터 이미지 src <-> 서버S3 URL 치환)한 전체 에디터 코드로 변환
//console.log('endContent',endContent);
} catch (error) {
console.log('이미지 업로드 실패', error);
}
}
return endContent;
};
return { urlArray, replaceImages};
};
2. 작성페이지 (커스텀 Hook사용하기)
function CreateForm({ lists, invoiceMaster }: FormProps) {
const { replaceImages,endContent } = useQuillImageReplacement();//1.커스텀Hook가져오기
..
const [content, setContent] = useState(''); //에디터 작성값 state
//완료 버튼 누를 시 실행되는 함수에 적용
const handlerCreate = async(values: any) => {
const updatedContent = await replaceImages(content); //2.커스텀Hook 함수에 매개변수 넣기
const totalValue = { ...values, content: updatedContent }; //3.커스텀Hook 리턴값(최종 content값)적용하기
console.log(totalValue, 'totalValue---');
};
'React' 카테고리의 다른 글
[React] React Quill 에디터 라이브러리 사용 5-수정 페이지 (0) | 2024.10.22 |
---|---|
[React] React Quill 에디터 라이브러리 사용 4-이미지 리사이즈(quill-image-resize) (1) | 2024.10.21 |
[React] React Quill 에디터 라이브러리 사용 2-이미지 처리/편집 중 (feat.react-quill-new) (0) | 2024.10.16 |
[React] React Quill 에디터 라이브러리 사용 1(feat.react-quill-new) (0) | 2024.10.15 |
[React] npm 대신 yarn 사용하기 (0) | 2024.10.14 |