<aside> 💡 프로젝트에 필요해서 사용하게 된 React-summernote summernote에서 제공하는 기본 이미지 업로드 기능은 기본 base64로 인코딩 저장하는 방식으로 데이터 관리를 위해 이미지 업로드 이벤트 메서드인 onImageUpload를 커스텀하여 원하는 경로(S3)에 저장 후 에디터에 반영되는 과정을 기록해본다.

</aside>

1. React-summernote 설치

yarn add react-summernote 
yarn add jquery // jquery가 없을 경우
yarn add bootstrap@4 // bootstrap이 없을 경우

2. webpack.config에 webpack.ProvidePlugin 추가

const webpack = require("webpack");
module.exports = {
	// ..
  plugins: [
    // 에디터를 위해 제이쿼리 추가
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
    }),
  ],
};

3. Summernote 에디터 컴포넌트 생성

  1. 이미지 업로드 커스텀
  2. onUploadImg라는 커스텀 이벤트 실행
  3. onUploadImg 이벤트의 결과를 subscribe
  4. onUploadImg 이벤트의 변경을 useEffect로 감지
  5. post api 후 리턴된 URL(common.data)을 insertImage 메서드로 에디터에 반영
  6. 변경 데이터 감지 !
import React, { useEffect } from "react";
import { useSelector } from "react-redux";

// imports for summernote
import ReactSummernote from "react-summernote";
import "react-summernote/dist/react-summernote.css";
import "react-summernote/lang/summernote-ko-KR";
import "bootstrap/js/dist/modal";
import "bootstrap/js/dist/dropdown";
import "bootstrap/js/dist/tooltip";
import "bootstrap/dist/css/bootstrap.css";

const SummernoteEditor = ({ defaultValue = "", onUploadImg }) => {
	// 3. onUploadImg 이벤트의 결과 subscribe
  const { common } = useSelector((state) => ({
    common: state.common.postImgUpload,
  }));

	// 4. onUploadImg 이벤트의 변경을 useEffect로 감지
  useEffect(() => {
    if (!common.data) return;
		// 5. post api 후 리턴된 URL(common.data)을 insertImage 메서드로 에디터에 반영
    ReactSummernote.insertImage(common.data);
  }, [common.data]);

  const onChange = (content) => {
		// 6. 변경 데이터 감지 !
    console.log("onChange ", content); // <p><img src="<https://storage.vicky.com/admin/20201029085457965.png>" style="width: 100px;"><br></p>
  };

	// 1. 이미지 업로드 커스텀
  const onImageUpload = (images) => {
    for (let i = 0; i < images.length; i++) {
      const file = images[i];
      if (file === undefined || file === null) {
        alert("파일을 먼저 골라주세요.");
        return;
      }
      const formData = new FormData();
      formData.append("data", file);
      onUploadImg(formData); // 2. onUploadImg라는 커스텀 이벤트 실행
    }
  };

  return (
    <div>
      <ReactSummernote
        children="내용을 입력해주세요."
        options={{
          lang: "ko-KR",
          height: 380,
          dialogsInBody: true,
          toolbar: [
            ["style", ["style"]],
            ["font", ["bold", "underline", "clear"]],
            ["fontname", ["fontname"]],
            ["para", ["ul", "ol", "paragraph"]],
            ["table", ["table"]],
            ["insert", ["link", "picture", "video"]],
            ["view", ["fullscreen", "codeview"]],
          ],
        }}
        onChange={onChange}
        onImageUpload={onImageUpload}
      />
    </div>
  );
};

export default SummernoteEditor;

4. 상위 컴포넌트에서 해당 컴포넌트 적용

<SummernoteEditor
	defaultValue={inputs.answer}
  onUploadImg={onUploadImg}
/>

5. 동작 화면

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55299707-94f4-49fd-97d6-e3de43075c23/chrome-capture.gif