Toast UI Editor에서 s3로 이미지 업로드 하는 방법

박진현·2022년 3월 5일
6

에러핸들링

목록 보기
26/28

구글링을 아무리 해봐도 클라이언트단에서 s3로 바로 이미지를 업로드하는 방법이 없어서 직접 해결한 결과를 공유합니다.

          <Editor
            initialValue={content}
            previewStyle="tab"
            height="70vh"
            initialEditType="markdown"
            useCommandShortcut={true}
            onChange={contentHandler}
            ref={editorRef}
          />

우선 Toast Editor에 ref를 걸고,

const editorRef = useRef<Editor>(null);

로 정의해줍니다.

그 다음 useEffect로 editorRef에 걸려있는 addImageHook을 제거한 후 새로운 커스텀훅을 달아줍니다.

  useEffect(() => {
    if (editorRef.current) {
      editorRef.current.getInstance().removeHook('addImageBlobHook');

      editorRef.current
        .getInstance()
        .addHook('addImageBlobHook', (blob, callback) => {
          const s3config = {
            bucketName: process.env.REACT_APP_BUCKET_NAME as string,
            region: process.env.REACT_APP_REGION as string,
            accessKeyId: process.env.REACT_APP_ACCESS_ID as string,
            secretAccessKey: process.env.REACT_APP_ACCESS_KEY as string,
          };
          const ReactS3Client = new S3(s3config);
          ReactS3Client.uploadFile(blob, uuidv4())
            .then((data) => callback(data.location, 'imageURL'))
            .catch((err) => (window.location.href = '/error'));
        });
    }
  }, []);

(s3라이브러리는 'react-aws-s3-typescript' 를 사용했습니다.)

위 코드대로 사용하시면 됩니다!

callback(data.location, 'imageURL') 은 업로드에 성공한 이미지의 URL주소를 담아 ![](주소) 형식으로 담아주는 함수를 의미합니다.

위에 과정을 따라하시다보면 Buffer가 없다는 에러가 나옵니다.

window.Buffer = window.Buffer || require('buffer').Buffer;

이 코드를 글로벌에 올려 주면 해결됩니다!

아래는 해당 컴포넌트 전체 코드입니다

import { useState, useRef, useEffect } from 'react';
import S3 from 'react-aws-s3-typescript';
import { v4 as uuidv4 } from 'uuid';
import { Editor } from '@toast-ui/react-editor';
import instance from 'utils/functions/axios';
import { PostData } from 'utils/functions/type';
import {
  EditorContainer,
  TitleUploadWrap,
  EditorWrap,
  Title,
  UploadButton,
} from './styled';

window.Buffer = window.Buffer || require('buffer').Buffer;

type PostDataType = {
  detailData?: PostData;
  boardId: number;
};

export default function PostEditor({ detailData, boardId }: PostDataType) {
  const [title, setTitle] = useState<string>('');
  const [content, setContent] = useState<string>('');
  const editorRef = useRef<Editor>(null);
  const currentUrl = window.location.href;
  const urlId = currentUrl.split('&postId=')[1];

  useEffect(() => {
    if (detailData) {
      editorRef.current?.getInstance().setMarkdown(detailData.content);
      setTitle(detailData.title);
      setContent(detailData.content);
    }
  }, []);

  useEffect(() => {
    if (editorRef.current) {
      editorRef.current.getInstance().removeHook('addImageBlobHook');

      editorRef.current
        .getInstance()
        .addHook('addImageBlobHook', (blob, callback) => {
          const s3config = {
            bucketName: process.env.REACT_APP_BUCKET_NAME as string,
            region: process.env.REACT_APP_REGION as string,
            accessKeyId: process.env.REACT_APP_ACCESS_ID as string,
            secretAccessKey: process.env.REACT_APP_ACCESS_KEY as string,
          };
          const ReactS3Client = new S3(s3config);
          ReactS3Client.uploadFile(blob, uuidv4())
            .then((data) => callback(data.location, 'imageURL'))
            .catch((err) => (window.location.href = '/error'));
        });
    }
  }, []);

  const uploadHandler = () => {
    if (window.location.pathname === '/writing') {
      instance
        .post(`/post?boardId=${boardId}`, { title: title, content: content })
        .then(
          (res) =>
            (window.location.href = `/detail?boardId=${boardId}&postId=${res.data.id}`),
        )
        .catch((err) => console.log(err));
    } else {
      instance
        .put(`/post?boardId=${boardId}&postId=${detailData?.id}`, {
          title: title,
          content: content,
        })
        .then(
          () =>
            (window.location.href = `/detail?boardId=${boardId}&postId=${urlId}`),
        )
        .catch((err) => console.log(err));
    }
  };

  const titleHandler = (event: { target: { value: string } }) => {
    setTitle(event.target.value);
  };
  const contentHandler = () => {
    setContent(editorRef.current?.getInstance().getMarkdown() || '');
  };
  return (
    <>
      <EditorContainer>
        <TitleUploadWrap>
          <Title>
            <input
              onChange={titleHandler}
              type="text"
              placeholder="제목을 입력해 주세요."
              value={title}
            />
          </Title>
          <UploadButton>
            <button onClick={uploadHandler}>등록</button>
          </UploadButton>
        </TitleUploadWrap>
        <EditorWrap>
          <Editor
            initialValue={content}
            previewStyle="tab"
            height="70vh"
            initialEditType="markdown"
            useCommandShortcut={true}
            // hooks={{addImageBlobHook: }}
            onChange={contentHandler}
            ref={editorRef}
          />
        </EditorWrap>
      </EditorContainer>
    </>
  );
}
profile
👨🏻‍💻 호기심이 많고 에러를 좋아하는 프론트엔드 개발자 박진현 입니다.

0개의 댓글