어제 하루종일 이거 검색하다가 000000같아서 정리함 후 영어권 검색해도 내가 원하는 정확한 방법이 없어서 영어권 사람들 볼지도 모르겠지만 아무튼 영어제목도 해놨는데 알아서 번역하겠지..?
작업 전, 아래의 조건에 만족해야합니다.
- react-quill 의 설치
- <ReactQuill/>의 onChange에 {(e)=>setState(e)} 의 형식으로 작성한 내용물이 state에 담겨야 합니다.
- <ReactQuill/>의 ref가 useRef로 선언되어있어야합니다.
- aws-sdk 의 설치
- https://docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html 에서 "사전필수작업", "CORS 구성", "웹페이지"까지 작업을 끝내면 됩니다.
아래 순서에 맞게 작업하면 됩니다.
- 이미지 핸들러를 만듭니다.
- 새로운 file타입의 input을 생성합니다.
- 1의 이미지 핸들러에 2의 input을 클릭하는 함수를 넣어줍니다.
- useState를 이용하여 2의 onChange에 읽어온 파일객체과 미리보기용 파일명(아주 길게 암호화 된 것)을 각각 저장합니다. (서로다른 state에 배열로 저장해줍시다.)
- 서버에 콘텐츠를 업로드할 버튼을 생성합니다.
- 5의 버튼 클릭시 아래의 동작이 이루어집니다.
- 반복문을 통하여 4에서 만든 파일객체를 s3에 업로드합니다.
- 4에서 만든 두개의 state 를 토대로 새로운 객체를 만들어 냅니다. {파일객체:암호화된 파일명, 파일객체:암호화된 파일명, ...}
- let 을 이용하여 초기값이 state에 저장된 <ReactQuill/>의 e 값인 변수를 선언합니다.
- forEach 와 split, join 을 이용하여 3에서 선언한 변수를 변경시켜줍니다.
이 아래부터는 위의 순서대로 작성한 코드를 이용하여 설명되어있습니다.
전부 하나의 js 파일 내에서 작성합니다. 위치는 알아서 알맞게 넣어주세요.
1) file 타입의 input 을 생성
<input
type="file"
accept=".png, .jpg, .jpeg"
ref={imageInputREF}
id="imgInput"
onChange={imageInputClick}
/>
2) input useRef
const imageInputREF = React.useRef();
3) 암호화된 이미지 주소와 이미지 객체를 저장할 useState
const [quillImage, setQuillImage] = React.useState([]);
const [quillImagebase, setQuillImagebase] = React.useState([]);
4) input onChange 함수 생성
const imageInputClick = () => {
const reader = new FileReader();
const file = imageInputREF.current.files[0];
if (file) {
reader.readAsDataURL(file);
reader.onloadend = () => {
const quill = QuillREF.current.getEditor();
const range = quill.getSelection()?.index;
quill.insertEmbed(range, "image", reader.result);
setQuillImagebase((prev) => [...prev, reader.result]);
setQuillImage((prev) => [...prev, file]);
};
}
};
reader는 파일을 읽어주는 함수로 readAsDataURL을 이용하여 파일을 읽어오고 onloadend로 파일을 읽은 후 실행할 수식을 적을 수 있습니다.
onloadend 수식에서는 아래와 같은 행동이 이루어집니다.
- 사용자의 커서가 있는 위치를 가져와 해당 위치에 읽은 임시 이미지파일주소가 들어간 img 태그를 삽입합니다. (quill.insertEmbed)
- 서로 다른 state에 임시 이미지파일 주소와 파일객체를 저장합니다. (이때, 파일 객체에는 파일 명칭 등이 들어가있습니다.)
5) 모듈 선언
const modules = React.useMemo(
() => ({
toolbar: {
container: "#toolbar",
handlers: {
image: imageHandler,
},
},
}),
[]
);
모듈을 선언해서 이미지 핸들러 함수를 넣어주고 <ReactQuill/> 에 넣어줍니다.
//이런 형태로 넣어주면 됩니다.
<ReactQuill
value={quillValue}
onChange={(e) => {
setQuillValue(e);
}}
modules={modules}
formats={formats}
ref={QuillREF}
/>
6) 이미지를 s3에 업로드하기위한 aws.config.update 설정
AWS.config.update({
region: "버킷이 존재하는 리전", // 버킷이 존재하는 리전을 문자열로 입력합니다. (Ex. "ap-northeast-2")
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: "인증키", // cognito 인증 풀에서 받아온 키를 문자열로 입력합니다. (Ex. "ap-northeast-2...")
}),
});
적당히 상단에 넣어줍니다. 저는 아래의 위치에 넣었습니다.
7) 업로드 버튼 생성 후 onClick 함수 생성
const uploadPost = () => {
if (quillImage.length > 0) {
quillImage.map((l, idx) => {
// S3 SDK에 내장된 업로드 함수
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: "업로드할 대상 버킷명",
Key: l.name, // 업로드할 파일명
Body: l, // 업로드할 파일 객체
},
});
const promise = upload.promise();
promise.then(
function (data) {
console.log("이미지 업로드에 성공했습니다.");
},
function (err) {
return console.log("오류가 발생했습니다: ", err.message);
}
);
});
}
let List = {};
for (let i = 0; i < quillImage.length; i++) {
List[quillImagebase[i]] = quillImage[i].name;
// List = {base:link,base:link, ...}
}
let content = quillValue;
quillImagebase.forEach((x, idx) => {
content = content
.split(x)
.join(
"https://star-project-post-storage.s3.ap-northeast-2.amazonaws.com/" +
String(quillImage[idx].name)
);
});
console.log("content:::", content);
};
위의 코드가 길기 때문에 쪼개보겠습니다.
- s3에 이미지 업로드
if (quillImage.length > 0) {
quillImage.map((l, idx) => {
// S3 SDK에 내장된 업로드 함수
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: "업로드할 대상 버킷명",
Key: l.name, // 업로드할 파일명
Body: l, // 업로드할 파일 객체
},
});
const promise = upload.promise();
promise.then(
function (data) {
console.log("이미지 업로드에 성공했습니다.");
},
function (err) {
return console.log("오류가 발생했습니다: ", err.message);
}
);
});
}
quillImage 는 state의 명칭으로 이미지 객체가 배열안에 저장되어있습니다.
map을 이용하여 이 안에 있는 모든 이미지를 저장해줍니다.
이때, 사용자가 이미지를 지울 경우를 대비해 <ReactQuill/>에서 onChange로 state에 저장한 값에 quillImage안의 객체들의 파일명칭과 동일한 것만 남겨주는 수식을 사용하여 위의 수식을 개선할 수 있습니다.
이는 이후 제 코드를 개선하면 이 글을 업데이트할 예정입니다.
promise 를 이용해서 업로드가 되면 실행할 함수를 지정해줄 수 있습니다.
위의 코드는 console.log를 통해 업로드 성공 여부를 알려주고있습니다.
아래는 이 글을 쓴 이유이며 저를 약 12시간동안 분노하게 만든 원흉입니다.
- src 주소 변경하기
react-quill을 이용하는 개발자라면 알겠지만 react-quill을 이용하여 사용자가 텍스트편집기에 입력한 값은 html 태그와 비슷하나 "문자열"로 얻을 수 있습니다.
예를 들어 편집기에 "가나다라"를 입력하고 <ReactQuill/>에서 onChange로 이 값을 단순하게 e값으로 얻으면 "<p>가나다라</p>"가 String 형식으로 나옵니다. 그리고 이미지또한 "<img src=""/>"로 나오는데 이때 src의 값이 굉장히 긴 텍스트로 나오기 때문에 우리는 이를 변경시켜줄 필요가 있습니다. 검색을 해보니 replace를 사용하는 경우도 있는데 이 글에서는 forEach, split, join 을 사용했습니다.
let List = {};
for (let i = 0; i < quillImage.length; i++) {
List[quillImagebase[i]] = quillImage[i].name;
// List = {base:name,base:name, ...}
}
우선 임시이미지src주소가 key값이고 이미지 이름이 value값인 새로운 객체를 만들어줍니다.
let content = quillValue;
quillImagebase.forEach((x, idx) => {
content = content.split(x).join(
"s3 이미지 링크 앞부분" +
String(quillImage[idx].name)
);
});
let을 이용하여 초기값이 <ReactQuill/>의 e값인 변수를 content 라 선언합니다.
이러면 content 는 "<p>어쩌구</p><img src="아주 긴 주소"/>"같은 형식의 문자열이 됩니다.
이 문자열에서 src값을 찾아서 split을 이용해 잘라내고, 잘라낸 부분에 join을 통해 새로운 주소를 넣고 다시 content에 넣어줍니다.
이 방법을 forEach문을 통해 반복해줍니다.
이후 console.log를 통해 content를 뽑으면 src가 잘 변경된걸 확인할 수 있습니다.
'이것저것 코딩공부' 카테고리의 다른 글
[Typescript] 5252, 타입스크립트.. 한번 해 보자고. (feat.노마드 코더 "Typescript로 블록체인 만들기") (4) | 2022.01.12 |
---|---|
[React] 무한스크롤, 가보자고. (feat. IntersectionObserver 겁나 편함) (0) | 2021.12.31 |
[React] useState 를 Dictionary 형태로 사용할때 (0) | 2021.10.26 |
React styled-component 형제버튼들 중 하나만 클래스 줄 때 (0) | 2021.10.15 |
내가 쓰기 편하게 모아두는 깃허브 git 명령어 (0) | 2021.10.01 |