본문 바로가기
React, AwsAmplify

(React, Aws S3)ToastUiEditor

by sarberos 2022. 1. 16.

사용 스택

React

AwsAmplfiy(Aws S3, Aws Api, Aws Cognito)

WYSIWYG : Toast Ui Editor

--

주의!

현재 사용중인 버전은 

    "@toast-ui/react-editor": "^2.5.4",
 
입니다.
3.0.0이상 버전에서 현재 url에 있는 &이 &로 자동 치환 되어서
이미지 로드가 안되는 현상이 있습니다.

특정 버전 패키지를 설치하기 위해서
npm install 모둘명@버전 을 해주시면 됩니다.
 

--

먼저 toast ui react를 다운로드 받습니다.

npm install toast-ui/react-editor@2.5.4

기본적인 ToastUiEditor에 이미지 저장시 S3로 업로드 되는 방식으로 커스텀 하였다.

 

Component/ToastUiEditor의 전체 코드는 다음과 같다.

import React, { useState, useEffect, useRef } from 'react';

import '@toast-ui/editor/dist/toastui-editor.css';
import '@toast-ui/editor/dist/i18n/ko-kr.js';
import { Editor } from '@toast-ui/react-editor';

import { Storage } from 'aws-amplify';

import {generateId} from "lib/common"

export default function ToastUiEditor(props) {

  const editorRef = useRef();
  const postId = props.postId;
  const initialValue = props.initialValue ? props.initialValue : null;

  if(props.editorFuns){
  props.editorFuns.getHtml = getHtml;
  props.editorFuns.getImagePaths = getImagePaths;
  }

  const [imageMap, setImageMap] = useState({});

  useEffect(() => {
    if (editorRef.current) {
      editorRef.current.getInstance().removeHook("addImageBlobHook");
      editorRef.current
        .getInstance()
        .addHook("addImageBlobHook", (blob, callback) => {
          (async () => {
            const imageUrl = await uploadImage(blob);
            callback(imageUrl, "iamge");
          })();

          return false;
        });
    }

    return () => {};
  }, [editorRef]);
  
  async function uploadImage(blob){
    const storagePath = 'origin/post/'+postId+"/"+generateId()+"."+blob.type.split("/")[1];
    const save = await Storage.put(storagePath, blob);
    if (save){
      const url = await Storage.get(storagePath);
      imageMap[storagePath] = url
      return url;
    }
  };

  function getImagePaths(){
    return Object.keys(imageMap)
  }

  function getHtml(){
    var html = editorRef.current.getInstance().getHtml();
    for (const [key, value] of Object.entries(imageMap)){
      html.replace(value,key);
      const replacedValue = value.replace(/&/gi, "&")
      html = html.replace(replacedValue, key);
    }
    return html
  }
  
  return (
    <div>
      <Editor
        placeholder={!initialValue && "내용을 작성해 주세요."}
        initialValue={initialValue}
        previewStyle="vertical"
        height="600px"
        initialEditType="wysiwyg"
        useCommandShortcut={true}
        ref={editorRef}
        language="ko-KR"
        hideModeSwitch={true}
      />
    </div>
);
}

 

기본 Toast Ui Editor은 이미지 업로드시 Base64형식으로 삽입된다. 이것을 그대로 저장할 경우 데이터베이스에 부담이 되기때문에 이미지는 Aws S3에 저장하고, 데이터베이스에는 path를 저장해 주기로 한다.

 

먼저 다음과 같이 useRef를 통해 Editor의 인스턴스를 가져올 준비를 한다.

 

  const editorRef = useRef();

...

  <Editor
    placeholder={!initialValue && "내용을 작성해 주세요."}
    initialValue={initialValue}
    previewStyle="vertical"
    height="600px"
    initialEditType="wysiwyg"
    useCommandShortcut={true}
    ref={editorRef}
    language="ko-KR"
    hideModeSwitch={true}
  />

그다음 useEffect를 통해

Editor의 인스턴스를 가져오고 기존에 base64로 저장하는 addImageBlobHook을 제거하고

S3에 저장하는 함수를 addImageblobHook을 사용해 대체해준다.

uploadImage 함수에서는 S3에 저장을 하고

그 path와 url을 모두 저장해둔다.

url은 editor에서 작성하면서 preview에 이미지를 볼수 있도록 하기 위해 사용되며

path는 editor의 콘텐츠와 같이 저장해서 후에 이미지를 불러오는데 사용된다.

 useEffect(() => {
    if (editorRef.current) {
      editorRef.current.getInstance().removeHook("addImageBlobHook");
      editorRef.current
        .getInstance()
        .addHook("addImageBlobHook", (blob, callback) => {
          (async () => {
            const imageUrl = await uploadImage(blob);
            callback(imageUrl, "iamge");
          })();

          return false;
        });
    }

    return () => {};
  }, [editorRef]);
  
  
  async function uploadImage(blob){
    const storagePath = 'origin/post/'+postId+"/"+generateId()+"."+blob.type.split("/")[1];
    const save = await Storage.put(storagePath, blob);
    if (save){
      const url = await Storage.get(storagePath);
      imageMap[storagePath] = url
      return url;
    }
  };

 

나중에 getImagePath()를 통해 업로드한 이미지의 path를 가져와 저장하고,

getHtml을 통해 내용물을 가져와서 저장해주면된다.

댓글