๊ด€๋ฆฌ ๋ฉ”๋‰ด

C-log

react-quill lib์ด๋ž€? react-quill์„ ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž ๋ณธ๋ฌธ

๐Ÿ’ป๊ฐœ๋ฐœ ๋…ธํŠธ/๋‚˜๋งŒ์˜ ์‹œํ—˜์ง€

react-quill lib์ด๋ž€? react-quill์„ ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž

4:Bee 2024. 3. 15. 00:51
728x90

์ด์ œ ๋‚˜๋งŒ์˜ ์‹œํ—˜์ง€๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์ œ์ž‘ํ•  ๊ฒƒ์ด๋‹ค. ๋ชจ๋“  ๊ธฐ๋Šฅ์˜ ํˆด์„ ์ผ์ผ์ด ์ œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ์ง€๋งŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ ๋น ๋ฅด๊ฒŒ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ข‹๊ฒŒ๋‹ค๊ณ  ํŒ๋‹จ์„ ํ–ˆ๋‹ค. FEํŒ€์›์ด ์•Œ์•„๋ด์ค€ ๊ฒฐ๊ณผ react-quill์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ •๋ณด์™€ ์ž๋ฃŒ๊ฐ€ ๋งŽ๋‹ค๊ณ  ์ถ”์ฒœ์„ ํ•ด์ฃผ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” react-qull ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•Œ์•„๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ์šฐ์„  react-quill ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆด npm์œผ๋กœ ์„ค์น˜ํ•˜๊ณ  ๊ธฐ๋ณธ์ ์ธ ์„ธํŒ…์„ ํ•ด๋ณด๊ฒ ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ์„ธํŒ…์„ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

# setting react-quill

 
import ReactQuill from "react-quill"
export default function EditTestSplit() {
  return (
    <>
      <h1>Test</h1>
      <ReactQuill></ReactQuill>
    </>
  )
}
 

์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €๋กœ ๋„์›Œ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.

๋ธŒ๋ผ์šฐ์ €์˜ ์ผ๋ถ€๋งŒ ์บก์ณํ•œ ์ด๋ฏธ์ง€์ด๋‹ค.

# import Css

๋ธŒ๋ผ์šฐ์ €์ƒ์˜ ๋ Œ๋”๋ง์ด ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ๋ฒ„ํŠผ๋“ค๊ณผ ์ด๋ฏธ์ง€์ธ ๊ฒƒ ๊ฐ™๋‹ค. ์ด๋ฏธ์ง€๊ฐ€ ์ด๋ ‡๊ฒŒ ์ค‘๊ตฌ ๋‚œ๋ฐฉ์œผ๋กœ ์ •๋ ฌ์ด ๋˜๋Š” ๊ฒƒ์ด ๋งž๋Š”์ง€ ์ฐพ์•„๋ณด๋‹ˆ css๋ฅผ ๊ผญ importํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค. ์•„๋ž˜๋Š” css๋ฅผ ์ถ”๊ฐ€ํ•ด ์ˆ˜์ •๋œ ์ฝ”๋“œ์ด๋‹ค.

 
import ReactQuill from "react-quill"
import "react-quill/dist/quill.snow.css"

export default function EditTestSplit() {
  return (
    <>
      <h1>Test</h1>
      <ReactQuill></ReactQuill>
    </>
  )
}
 

css๋ฅผ importํ•˜๋ฉด ๋‚˜์˜ค๋Š” ๊ฒฐ๊ณผ ์ด๋‹ค.

์ด์ œ quill ๋ผ์ด๋ธŒ๋Ÿฌ๊ฐ€ ์ œ๋Œ€๋กœ import๋œ ๊ฒƒ์„ ํ™•์ธ ํ–ˆ๊ณ  ํ•ด๋‹น ํŽธ์ง‘๊ธฐ์˜ ํˆด๋“ค์„ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ๋“ค๋งŒ ์ปค์Šคํ…€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ํˆด๋“ค์„ ์ปค์Šคํ…€ํ•œ ์ฝ”๋“œ์ด๋‹ค.

 
import ReactQuill from "react-quill"
import "react-quill/dist/quill.snow.css"

export default function EditTestSplit() {

  const modules = {
    toolbar: {
      container: [
        ["image"],
        [{ header: [1, 2, 3, 4, 5, false] }],
        ["bold", "underline"],
      ],
    },
  };

  return (
    <>
      <h1>Test</h1>
      <ReactQuill
        style={{ width: "800px", height: "600px" }}
        modules={modules}
      />
    </>
  )
}
 

์šฐ์„  ReactQuill ์ปดํฌ๋„ŒํŠธ์— modules๋ผ๋Š” ํ”„๋กœํผํ‹ฐ์—๋Š” ํŽธ์ง‘๊ธฐ์—์„œ ์‚ฌ์šฉํ•  ๋‹ค์–‘ํ•œ ๋ชจ๋“ˆ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ๋Š” modules ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ด๋ฏธ์ง€ ์‚ฝ์ž…, ํ—ค๋” ์Šคํƒ€์ผ ์„ค์ •, ๊ทธ๋ฆฌ๊ณ  ๋ณผ๋“œ์ฒด ๋ฐ ๋ฐ‘์ค„๊ณผ ๊ฐ™์€ ํ…์ŠคํŠธ ์Šคํƒ€์ผ ์ง€์ •๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์„ ํŽธ์ง‘๊ธฐ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ ๊ฑด ์ด๊ฒƒ์„ ์—๋””ํ„ฐ ์ƒํƒœ์—์„œ ์ฝ๊ธฐ ์ƒํƒœ๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜์žˆ๊ฒŒ ์ œ์ž‘์„ ํ•ด์•ผํ•œ๋‹ค.

# useState๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ณ€ํ™”๋œ ๊ฐ’๋“ค์„ ์ ์šฉํ•˜๊ธฐ

useState๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‚ด๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’๋“ค์„ log๋กœ ๋ฐ›์•„ ๋ณด์ž. ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” useState๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•ด๋‹น input ํ”„๋กœํผํ‹ฐ์˜ onChange๋ฅผ ๋„ฃ์–ด์„œ ๊ฐ’์„ ๋ฐ›์•„ ์™€์•ผํ•œ๋‹ค. ๋จผ์ € useState๋ฅผ ์ƒ์„ฑํ•ด๋ณด์ž. ์•„๋ž˜ ์ฝ”๋“œ๋Š” useState์™€ ํ•„์š”ํ•œ input๊ณผ ๋ฒ„ํŠผ๋“ค๋งŒ ์‚ฝ์ž…ํ•œ ์ฝ”๋“œ์ด๋‹ค.

import { useState } from "react"
import ReactQuill from "react-quill"
import "react-quill/dist/quill.snow.css"
  ...
  const [content, setContent] = useState("");
  const [title, setTitle] = useState("");


  return (
    <>
      <div>
        <div>
          <label htmlFor="title">Title</label>
          <input id="title" type="text"></input>
          <h1>Test</h1>
          <ReactQuill
            style={{ width: "800px", height: "600px" }}
            modules={modules}
          />
        </div>
      </div>
      <div>
        <button style={{ marginTop: "50px" }}>์ œ์ถœ</button>
      </div>
    </>
  )
}

๊ฐ ํ•„์š”ํ•œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž. ์•„๋ž˜ ์ฝ”๋“œ๋Š” ํ•„์š”ํ•œ ํ•ธ๋“ค๋ง ํ•จ์ˆ˜๋งŒ ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ด๋‹ค.


  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const handleChange = (e) => {
    setTitle(e.currentTarget.value);
    setContent(e.currentTarget.value);
    console.log(title);
    console.log(content);
  }

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์ด์ œ ์ ์ ˆํ•œ ์œ„์น˜์— onChange๋ฅผ ์ด์šฉํ•ด์„œ jsx์— ๋Œ€์ž…์„ ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์•„.

return (
    <>
      <div>
        <div>
          <label htmlFor="title">Title</label>
          <input id="title" type="text" onChange={handleChange}></input>
          <h1>Test</h1>
          <ReactQuill
            style={{ width: "800px", height: "600px" }}
            modules={modules}
            onChange={handleChange}
          />
        </div>
      </div>
      <div>
        <button style={{ marginTop: "50px" }}>์ œ์ถœ</button>
      </div>
    </>
  )

์œ„ ์™€๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š”๋ฐ ๊ตฌ์ฒด์ ์ธ ์—๋Ÿฌ์™€ ์ด์œ ๋Š” ์•„๋ž˜ ๋”๋ณด๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•ด๋ณด์ž.

๋”๋ณด๊ธฐ

์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚œ ์ด์œ ๋Š” react-quill์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— onChange๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋Š” ์ด๋ฒคํŠธ๋กœ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค.  react-quill ์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—๋””ํ„ฐ์˜ ๋‚ด์šฉ์„ ๊ด€๋ฆฌํ•˜๊ณ , ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’์€ ๋‚ด๋ถ€์  ๋ณต์žกํ•œ ๊ฐ์ฒด ๋‹ค.

## ๋‚ด๋ถ€์  ๋ณต์žกํ•œ ๊ฐ์ฒด

 react-quill์€ quill ์—๋””ํ„ฐ์˜ react ๋ž˜ํผ(wrapper)์ด๋ฉฐ, quill ์—๋””ํ„ฐ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐ ๋ Œ๋”๋ง์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋”ฐ๋ผ์„œ,  react-quill์˜ onChange ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ํ‘œ์ค€ HTML ์ž…๋ ฅ ์š”์†Œ์˜ ์ด๋ฒคํŠธ์™€๋Š” ๋‹ค๋ฅธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ๋Š” content: ์—๋””ํ„ฐ์˜ ํ˜„์žฌ ๋‚ด์šฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‚˜ Delta ๊ฐ์ฒด์ด๋‹ค. Delta ๊ฐ์ฒด๋Š” quill์—์„œ ๋‚ด์šฉ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ํ‘œ์ค€ ํฌ๋งท์ด๋‹ค.

 
  function handleChange(content, delta, source, editor) {
    console.log(editor.getContents()); // ์—๋””ํ„ฐ์˜ ํ˜„์žฌ ๋‚ด์šฉ์„ Delta ๊ฐ์ฒด๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    console.log(content); // ์—๋””ํ„ฐ์˜ ํ˜„์žฌ ๋‚ด์šฉ์„ HTML ๋ฌธ์ž์—ด ๋˜๋Š” Delta ๊ฐ์ฒด๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  }
 

์œ„ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ • ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด๋œ๋‹ค.


  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const handleTitleChange = (e) => {
    setTitle(e.currentTarget.value);
    console.log(title);
  }

  const handleTextChange = (text) => {
    setContent(text);
    console.log(text ;
  }
 

์ „์ฒด ์ฝ”๋“œ๋Š” ์•„๋ž˜ ๋”๋ณด๊ธฐ๋ฅผ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

๋”๋ณด๊ธฐ
import { useState } from "react";
import ReactQuill from "react-quill"
import "react-quill/dist/quill.snow.css"

export default function EditTestSplit() {

  const modules = {
    toolbar: {
      container: [
        ["image"],
        [{ header: [1, 2, 3, 4, 5, false] }],
        ["bold", "underline"],
      ],
      userOnly: true
    },
    clipboard: {
      // ํด๋ฆฝ๋ณด๋“œ ๋ชจ๋“ˆ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋ณต์‚ฌ ๋ฐ ๋ถ™์—ฌ๋„ฃ๊ธฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
      matchVisual: false,
    },
    history: {
      // ํžˆ์Šคํ† ๋ฆฌ ๋ชจ๋“ˆ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ undo ๋ฐ redo ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
      delay: 2000,
      maxStack: 500,
      userOnly: false,
    },
    keyboard: {
      // ํ‚ค๋ณด๋“œ ๋ชจ๋“ˆ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ
      bindings: {},
    },
  };

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const handleTitleChange = (e) => {
    setTitle(e.currentTarget.value);
    console.log(title);
  }

  const handleTextChange = (text) => {
    setContent(text);
    console.log(text ;
  }

  // const handleTitleChange = (e) => {
  //   setTitle(e.currentTarget.value);
  //   console.log(title); //์ž…๋ ฅ ํ™•์ธํ•˜๋Š” log
  // };

  // const handleSubmit = async () => {
  //   const date = new Date();
  //   try {
  //     await createPost({
  //       title: title,
  //       content,
  //       date,
  //     }).then((res) => console.log(res))
  //   } catch (error) {
  //     console.log(error);
  //   }
  // };

  return (
    <>
      <div>
        <div>
          <label htmlFor="title">Title</label>
          <input id="title" type="text" onChange={handleTitleChange}></input>
          <h1>Test</h1>
          <ReactQuill
            style={{ width: "800px", height: "600px" }}
            modules={modules}
            onChange={handleTextChange}
          />
        </div>
      </div>
      <div>
        <button style={{ marginTop: "50px" }}>์ œ์ถœ</button>
      </div>
    </>
  )
}

# ์ž…๋ ฅ textArea ๋น„ํ™œ์„ฑํ™” ๊ทธ๋ฆฌ๊ณ  quill ์ƒ์„ฑ์ž์™€ useEffect

์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ์ž…๋ ฅํ•œ textArea ๋ถ€๋ถ„์„ ๋น„ํ™œ์„ฑํ™” ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์šฐ์„  reactQuill๊ณผ Quill์˜ ์ฐจ์ด๋ฅผ ์•Œ์•„์•ผํ•œ๋‹ค. Quill์€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

 
import { useEffect, useRef } from 'react';
import Quill from 'quill';
import 'quill/dist/quill.snow.css';

export default function EditTestSplit() {
  const editorRef = useRef(null);

  useEffect(() => {
    if (editorRef.current) {
      const quill = new Quill(editorRef.current, {
        theme: 'snow',
        modules: {
          toolbar: true // ํˆด๋ฐ”๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.
        }
      });
    }
  }, []);

  return (
    <div>
      <div ref={editorRef} style={{ display: 'none' }}></div>
    </div>
  );
}

์œ„์˜ ์ฝ”๋“œ๋ฅผ์‚ดํŽด๋ณด๋ฉด ๊ธฐ์กด react-quill์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  quill์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์„ธํŒ…์„ ํ–ˆ๋‹ค. quill์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” uesEffect๋กœ quill์„ ์ƒ์„ฑ์ž๋กœ ์ƒ์„ฑํ•˜๊ณ  ์กฐ์ž‘์„ ํ•ด์•ผํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ DOM ์š”์†Œ์— ๋Œ€ํ•œ ์กฐ์ž‘์€ react ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋œ ํ›„์— ์ด๋ฃจ์–ด์ ธ์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ณดํ†ต useEffect ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” ์˜๋ฌธ์„ ๊ฐ€์งˆ ๊ฒƒ์ด๋‹ค. ์™œ DOM์— ์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ์ง‘์ฐฉ์„ ํ•˜๋ฉด์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š”๊ฐ€ ๋ง์ด๋‹ค. ์šฐ์„  quill ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” js์— ํ™˜๊ฒฝ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ ๊ฒƒ์ด๋‹ค.  ์ฆ‰, ์ผ๋ฐ˜์ ์œผ๋กœ js์—์„œ DOM์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ง์€ quill ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ react์˜ ํŠน์ง•์ธ virtualDOM์„ ๊ณ ๋ คํ•ด์„œ ์ œ์ž‘๋œ ๊ฒƒ์ด ์•„๋‹ˆ๊ธฐ์— react-quill์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆ ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ react์—์„œ DOM์— ์ ‘๊ทผ์„ ํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์ด ์žˆ๋‹คํ•˜๋ฉด react์—์„œ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. 

useEffect ๋‚ด์—์„œ quill ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ  ํ•ด๋‹น DOM ์š”์†Œ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์— quill ์ƒ์„ฑ์ž๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ์ดˆ๊ธฐํ™”๋ฅผ ํ•˜๋Š” ์ด์œ ๋Š” ์•ž์„œ ๋งํ–ˆ์ง€๋งŒ DOM์— ์ง์ ‘์ ์œผ๋กœ ์‚ฝ์ž…์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.  quill๊ณผ ๊ฐ™์€ WYSIWYG ์—๋””ํ„ฐ๋Š” DOM ์š”์†Œ์— ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์ž‘๋™ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ DOM์š”์†Œ์— ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ฒฐ์ด ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์š”์†Œ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์— quill์„ ์ดˆ๊ธฐํ™”ํ•ด์•ผ๋งŒ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•œ๋‹ค. react์—์„œ๋Š” DOM ์š”์†Œ์˜ ์ƒ์„ฑ ๋ฐ ์กฐ์ž‘์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด virtualDOM์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ปดํฌ๋„ŒํŠธ์˜ ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„์— ์‹ค์ œ DOM ์š”์†Œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ์ดํ›„์—๋Š” useEffect๋‚˜ componentDidMount์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DOM ์š”์†Œ์— ๋Œ€ํ•œ ์ดˆ๊ธฐํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ดˆ๊ธฐํ™” ์ด์ „์— DOM ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด quill์ด ์—ฐ๊ฒฐ๋  ๋Œ€์ƒ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๋ก  ์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ํ™˜๊ฒฝ์— quill ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ผ์›Œ ๋งž์ถฐ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์€ ํฐ ๋ฌด๋ฆฌ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜์žˆ์—ˆ๋‹ค.

์•„๋ž˜ ๋”๋ณด๊ธฐ๋ฅผ ํ†ตํ•ด์„œ ์–ธ๋งˆ์šดํŠธ์™€ ๋งˆ์šดํŠธ์˜ ๊ด€ํ•œ ๊ธ€์„ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”๋ณด๊ธฐ

## react์˜ ์–ธ๋งˆ์šดํŠธ์™€ ๋งˆ์šดํŠธ

๋งˆ์šดํŠธ๋Š” React ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์‚ฝ์ž…๋˜๋Š” ๊ณผ์ •์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ฆ‰ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๋ธŒ๋ผ์šฐ์ €์˜ ์‹ค์ œ DOM์— ์ถ”๊ฐ€๋˜๋Š” ์‹œ์ ์ด๋‹ค. React์—์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ์œผ๋กœ DOM์— ์‚ฝ์ž…๋  ๋•Œ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ๋ จ์˜ ๋‹จ๊ณ„๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

1. constructor(): ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ์„ฑ์ž ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

2. static getDerivedStateFromProps(): ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด props๋กœ๋ถ€ํ„ฐ ์ƒํƒœ๋ฅผ ๋„์ถœํ•˜๊ฑฐ๋‚˜ ๋™๊ธฐํ™”ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฉ”์„œ๋“œ๋Š” ํ•„์ˆ˜๋Š” ์•„๋‹ˆ๋‹ˆ๋‹ค.

3. render(): ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ์—์„œ๋Š” React ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํžŒ๋‹ค.

4. componentDidMount(): ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์‚ฝ์ž…๋œ ์งํ›„์— ํ˜ธ์ถœ๋œ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์ดˆ๊ธฐํ™” ์ž‘์—…, ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋“ฑ์˜ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์ด ๋‹จ๊ณ„๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ DOM์— ๋งˆ์šดํŠธ๋˜์—ˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ์˜ ๋งˆ์šดํŠธ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ์ดํ›„์—๋Š” ์—…๋ฐ์ดํŠธ๋‚˜ ์–ธ๋งˆ์šดํŠธ์™€ ๊ด€๋ จ๋œ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

# React-Quill๊ณผ Quill์˜ ์ฐจ์ด

๊ฐ€์žฅ ํฐ ์ฐจ์ด๋Š” ์ผ๋ฐ˜ quill์€ ์ˆœ์ˆ˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ž‘์„ฑ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ react-quill์€ react ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ quill์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก react ์ปดํฌ๋„ŒํŠธ๋กœ ๋ž˜ํ•‘๋œ ๊ฒƒ์ด๋‹ค. react-quill์€ quill ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ react ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋” ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ react-quill์€ react ์ปดํฌ๋„ŒํŠธ๋กœ ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ react ์ปดํฌ๋„ŒํŠธ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” react ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ฑฐ๋‚˜ react์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋ฉด์— ์ผ๋ฐ˜ quill์€ react ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ react์™€์˜ ํ†ตํ•ฉ์ด ๋” ์–ด๋ ต๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  react-quill์ด ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๋งŒํผ ์กฐ์ž‘ํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์œ„์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ ๋“ฏ event์ฒ˜๋ฆฌ๊ฐ€ ์‰ฝ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‚ด๋ถ€์˜ ๋ณต์žก์„ฑ์ด ์žˆ์–ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ํŒŒํ•ด์น˜๊ณ  ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค. 

* ๋ž˜ํ•‘์ด๋ž€?

๋”๋ณด๊ธฐ

## ๋ž˜ํ•‘์ด๋ž€?

react ์ปดํฌ๋„ŒํŠธ์—์„œ์˜ ๋ž˜ํ•‘์€ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋‚˜ ์š”์†Œ๋ฅผ ๊ฐ์‹ธ์„œ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ฉ๋‹ˆ๋‹ค.


* Reference

 

React-Quill ์‚ฌ์šฉํ•˜๊ธฐ(1)

ReactQuill ์‚ฌ์šฉํ•˜๊ธฐ

velog.io

### ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ค„์ด๊ธฐ

 

Quill React ์—๋””ํ„ฐ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ (์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์‚ฌ์ด์ฆˆ ์กฐ์ ˆ)

Quill Editor๋ž€? ๊ณต์‹๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด Quill Editor๋Š” ๋ชจ๋˜ ์›น์„ ์œ„ํ•œ ์˜คํ”ˆ์†Œ์Šค WYSIWYG ์—๋””ํ„ฐ๋ผ๊ณ  ์†Œ๊ฐœํ•˜๊ณ  ์žˆ๋‹ค. WYSIWYG๋Š” What You See Is What You Get์˜ ์•ฝ์ž๋กœ, ํŽธ์ง‘ ๊ณผ์ •์—์„œ์˜ ํ™”๋ฉด ํฌ๋งท์ด ์ตœ์ข… ์™„์„ฑ๋ณธ์ด๋ž‘

mingeesuh.tistory.com


# ๊ฒฐ๋ก 

์šฐ๋ฆฌ๊ฐ€ ํ˜„์žฌ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์— react-quill๊ณผ quill์€ ์—ฌ๋Ÿฌ๋ฉด์—์„œ ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค. ๊ธฐ๋ณธ์ ์ธ ๊ฒŒ์‹œํŒ๊ณผ ๊ฐ™์€ ๋‹จ์ˆœํ•œ ํ˜•ํƒœ๋ผ๋ฉด ๊ฐ„๋‹จํ•˜๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ ์—ฌ๋Ÿฌ ์ƒํ™ฉ๊ณผ ์ƒํƒœ์—์„œ ๋™์ž‘๊ตฌํ˜„์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์—ฌ๋Ÿฌ ์ œํ•œ์„ ๋ฐ›๋Š”๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐœ๋ฐœ ์‹œ๊ฐ„์„ ๋‹จ์ถ• ์‹œํ‚ค๋Š” ๊ฒƒ์€ ์‚ฌ์‹ค์ด์ง€๋งŒ ์ƒํ™ฉ๊ณผ ํ˜„ ๊ฐœ๋ฐœ ๋ชฉ์ ์— ๋”ฐ๋ผ ์‹œ๊ฐ„์˜ ๋‹จ์ถ•๊ณผ ์ง€์—ฐ์€ ๋‹ฌ๋ผ์ง„๋‹ค๋Š” ๊ฒƒ์„ ์ฑ„๊ฐํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฐœ๋ฐœ ๋ชฉ์ ์€ ์ข€ ๋” ์ž์œจ์„ฑ์ด ํ™•๋ณด๋˜๋ฉด์„œ ์„ธ์‹ฌํ•˜๊ฒŒ ์กฐ์ž‘๊ณผ ๋™์ž‘์ด ๊ฐ€๋Šฅํ•ด์•ผํ•œ๋‹ค. ํ˜„์žฌ react-quill๊ณผ quill๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์กฐ์‚ฌํ•˜๊ณ  ์—ฌ๋Ÿฌ ์‹คํ—˜๊ณผ ์ ์šฉํ•˜๋Š”๋ฐ ๋“ค์–ด๊ฐ„ ์‹œ๊ฐ„์ด 4์ผ์ด๋‚˜ ๋˜์—ˆ๋‹ค๋Š” ์ ์„ ๊ณ ๋ คํ•˜๋ฉด ์ง์ ‘ editor์™€ tool๋“ค์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ ์„ ํƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค. ์ •๋ฆฌ ํ•˜์ž๋ฉด ์šฐ๋ฆฌ๊ฐ€ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์˜ ์ด์œ ๋Š” ๋‹ค์Œ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ œ์•ฝ
    1. react-quill์€ onClick๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค. ์ด๋Š” ์ฆ‰, ์šฐ๋ฆฌ ๊ฐœ๋ฐœ์˜ ์ž์œจ์„ฑ์„ ๋–จ์–ดํŠธ๋ฆฐ๋‹ค.
    2. quill์€ ๋ฐ”๋‹๋ผ js์— ์ ํ•ฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. react์—์„œ ๊ตฌ๋™์€ ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ quill์€ DOM์— ์ง์ ‘์ ์ธ ์‚ฝ์ž… ๊ณผ์ •์ด ํ•„์š”ํ•ด์„œ ๋งˆ์šดํŠธ๋ฅผ ๊ณ ๋ คํ•ด useEffect๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ค˜์•ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘์„ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ editor์™€ tool์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” react-quill๊ณผ quill์„ ์‹คํ—˜ํ•ด๋ดค๋˜ ๋‚ด์šฉ์„ ๋‹ด๊ณ  ์žˆ์ง€๋งŒ ๋‚ด๊ฐ€ ์‹คํ—˜ํ–ˆ๋˜ ๋ชจ๋“  ์ˆ˜ํ–‰๊ณผ์ •๋“ค์„ ๋‹ด์•„ ๋‚ด์ง€ ๋ชปํ–ˆ๋‹ค. ๋„ˆ๋ฌด ๋งŽ์€ ์ •๋ณด๋“ค์„ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋Š˜์–ด ๋†“๊ธฐ ๋ณด๋‹ค. ๊ณผ์ • ์ค‘ ์ค‘์š”ํ–ˆ๋˜ ๋ถ€๋ถ„๋“ค๋งŒ ๋ฝ‘์•„ ์ž‘์„ฑ์„ ํ–ˆ๋‹ค.

728x90
Comments