이것저것 코딩공부

React quill change image file name (s3) before upload content - 리액트 퀼 s3에 업로드한 이미지로 퀼 컨텐츠의 src 주소를 변경하는 법

김쨔뿌 2021. 11. 21. 15:22

어제 하루종일 이거 검색하다가 000000같아서 정리함 후 영어권 검색해도 내가 원하는 정확한 방법이 없어서 영어권 사람들 볼지도 모르겠지만 아무튼 영어제목도 해놨는데 알아서 번역하겠지..?

 

작업 전, 아래의 조건에 만족해야합니다.

아래 순서에 맞게 작업하면 됩니다. 

  1. 이미지 핸들러를 만듭니다.
  2. 새로운 file타입의 input을 생성합니다.
  3. 1의 이미지 핸들러에 2의 input을 클릭하는 함수를 넣어줍니다.
  4. useState를 이용하여 2의 onChange에 읽어온 파일객체과 미리보기용 파일명(아주 길게 암호화 된 것)을 각각 저장합니다. (서로다른 state에 배열로 저장해줍시다.)
  5. 서버에 콘텐츠를 업로드할 버튼을 생성합니다.
  6. 5의 버튼 클릭시 아래의 동작이 이루어집니다.
    1. 반복문을 통하여 4에서 만든 파일객체를 s3에 업로드합니다.
    2. 4에서 만든 두개의 state 를 토대로 새로운 객체를 만들어 냅니다. {파일객체:암호화된 파일명, 파일객체:암호화된 파일명, ...}
    3. let 을 이용하여 초기값이 state에 저장된 <ReactQuill/>의 e 값인 변수를 선언합니다.
    4. 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 수식에서는 아래와 같은 행동이 이루어집니다.

  1. 사용자의 커서가 있는 위치를 가져와 해당 위치에 읽은 임시 이미지파일주소가 들어간 img 태그를 삽입합니다. (quill.insertEmbed)
  2. 서로 다른 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가 잘 변경된걸 확인할 수 있습니다.