Frontend ๊ฐœ๋ฐœ์ž - hyo.loui

Project: ๐Ÿšฉcodefolio - Toast UI Editor ์‚ฌ์ง„ ์—…๋กœ๋“œ ๋ณธ๋ฌธ

Project

Project: ๐Ÿšฉcodefolio - Toast UI Editor ์‚ฌ์ง„ ์—…๋กœ๋“œ

hyo.loui 2023. 2. 16. 03:25

๐ŸŽฏ๋ชฉ์  :

์—๋””ํ„ฐ ๊ธฐ์กด์˜ ๋ฌธ์ œ์  ๊ฐœ์„ 
(์‚ฌ์ง„ ์—…๋กœ๋“œ ์‹œ ๋ทฐ์–ด์—์„œ ์ด๋ฏธ์ง€๊ฐ€ string ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ๋˜์–ด์ง)

๊ฐœ์„  : image url๊ณผ file name์œผ๋กœ ๋ณด์—ฌ์งˆ ์ˆ˜ ์žˆ๋„๋ก

 

** ์ตœ์ข… ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„ ๋งค์šฐ ๋ฐ”์œ ๊ด€๊ณ„๋กœ ์šฐ์„  ์ค‘์š” ์ฝ”๋“œ๋งŒ ์ ์–ด๋†“์•˜์Œ**


1.  UI tag

 

<Editor
  ref={editorRef}
  initialValue={postContent ?? null}
  previewStyle="vertical"
  // previewHighlight={false}
  height="600px"
  initialEditType="markdown"
  useCommandShortcut
  toolbarItems={toolbarItems}
  language="ko-KR"
  plugins={[colorSyntax]}
  hooks={{
    // @ts-ignore
    addImageBlobHook: addImage,
  }}
  onChange={() => handleOnEditorChange()}
/>

hooks ์‚ฌ์šฉํ•˜์—ฌ addImage ๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ปค์Šคํ…€ํ•˜์—ฌ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค.

 


2. addImage (์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ „์ฒด ๊ณผ์ •)

  const addImage = async (blob: File, dropImage: HookCallback) => {
    const img = await compressImg(blob); // ์ด๋ฏธ์ง€ ์••์ถ•
    if (!img) return;
    const url = await uploadImage(img); // ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€ ์„œ๋ฒ„ url
    if (!url) return;
    dropImage(url, `${blob.name}`); // ์—๋””ํ„ฐ์— ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
  };

์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋„ˆ๋ฌด ํฌ๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„ํ—˜๊ณผ, 1ํšŒ์„ฑ์œผ๋กœ ๋ณด์—ฌ์ค„ ๋‚ด์šฉ์„ ์ˆ˜์‹œ๋กœ ์„œ๋ฒ„ํ†ต์‹ ํ•˜๋Š” ๋ฐ์—

๋ถ€๋‹ด์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ์ง€ ์••์ถ•์„ ํ–ˆ๊ณ , url ์—๋Š” ์„œ๋ฒ„ํ†ต์‹ ์ด ๋œ img๋ฅผ ๋ฐ›๊ณ ์žˆ๋‹ค.

 


3. uploadImage (์—…๋กœ๋“œ)

 

  // ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
  const uploadImage = async (blob: File) => {
    try {
      const imgPath = crypto.randomUUID();
      await supabase.storage.from("post-image").upload(imgPath, blob);

      // ์ด๋ฏธ์ง€ ์˜ฌ๋ฆฌ๊ธฐ
      const urlResult = await supabase.storage
        .from("post-image")
        .getPublicUrl(imgPath);
      return urlResult.data.publicUrl;
    } catch (error) {
      console.log(error);
      return false;
    }
  };

ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์—๋””ํ„ฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์ง„์„ supabase๋ฅผ ํ™œ์šฉํ•˜์—ฌ

๋ฐ์ดํ„ฐํ˜•ํƒœ๋ฅผ ์ €์žฅํ•ด ์ค€๋‹ค.

 


4. compressImg (์••์ถ•)

 

// ์ด๋ฏธ์ง€ ์••์ถ•
const compressImg = async (blob: File): Promise<File | void> => {
const options = {
  maxSize: 1,
  initialQuality: 0.55, // initial 0.7
};
const result = await imageCompression(blob, options)
  .then((res) => res)
  .catch((e) => console.log(e, "์••์ถ• ์—๋Ÿฌ"));
return result;
};

์ด๋ฏธ์ง€๋ฅผ ์••์ถ•ํ•˜๋ ค๋ฉด imageCompression์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ import ํ•œ ํ›„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

 

 


5. handleOnEditorChange

 

  const handleOnEditorChange = () => {
    // ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    const editorText = editorRef.current?.getInstance().getMarkdown();
    if (editorText === " " || editorText === "" || editorText === undefined) {
      return;
    }
    // HTML ๋Œ€์‹ ์— Markdown์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
    setPostContent(editorText);
  };

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ํ•ด๋‹น ์—๋””ํ„ฐ๊ฐ€ ์•„๋ฌด๋Ÿฐ ํƒ€์ดํ•‘ ์—†์ด

posting ํ–ˆ์„ ๋•Œ๋ฅผ ์žก์•„์ค€๋‹ค. ์ดํ›„ ๋ฌธ์ œ ์—†๋‹ค๋ฉด state๋กœ editorText๋ฅผ ์ „์†กํ•œ๋‹ค.