jh.nrtv

Next.js 프로젝트에 Quill Editor 사용하기 본문

Next.js

Next.js 프로젝트에 Quill Editor 사용하기

wlgus3 2023. 7. 25. 00:26

 Next.js로 개발하고 있는 프로젝트의 게시판 작성기능에 

WYSIWYG (What You See Is What You Get) 에디터를 구현해야 할 필요성을 느꼇다. 

 

위지위그는 HTML 태그를 사용자가 입력할 필요 없이 에디터 내장 기능을 이용해서 원하는 글의 모양을 만들면 자동으로 HTML 방식으로 변환하는 편집기를 의미한다. 

여러 docs나 블로그 글 게시기능 등에 기본적으로 탑재되어 있기 때문에 익숙한 형태이다. 

 

저번 React 프로젝트에서는 CKeditor를 사용했는데 에러가 너무 많이 발생해서 ㅜㅜ

이번에는 next와 더 호환성이 좋다는 Quill을 사용해보고자 한다. 

 

Quill - Your powerful rich text editor

Sailec Light Sofia Pro Slabo 27px Roboto Slab Inconsolata Ubuntu Mono Quill Rich Text Editor Quill is a free, open source WYSIWYG editor built for the modern web. With its modular architecture and expressive API, it is completely customizable to fit any ne

quilljs.com

Quill Editor 설치하기

npm install quill@1.3.7 @types/quill

 

Editor 기능 들어간 게시글 작성 컴포넌트 생성 

 

일단 window object가 있는지 확인하고 require로 import 하는 과정을 거친다.  (그냥 import하면 document undefined 에러 발생)

const Quill = typeof window === "object" ? require("quill") : () => false;

 

1. 기존의 코드는  <form> 태그 안에 <input> 태그가 여러 개 존재하고 <button>태그 클릭과 동시에 전부 자동으로 서버로 보내주는 방식이었기에 코드가 매우 간결했지만 Quill은 <input>태그의 형태로 입력을 받지 않으며 결과값을 delta라고 하는 형식으로 뱉어내기 때문에 코드 전체가 수정되어야 했다. 

 

Delta - Quill

Delta Deltas are a simple, yet expressive format that can be used to describe Quill’s contents and changes. The format is a strict subset of JSON, is human readable, and easily parsible by machines. Deltas can describe any Quill document, includes all te

quilljs.com

 

일단 아래 코드까지가 Quill을 화면에 띄우기까지의 코드이다. 

// NewPostbyQuill.tsx

"use client";
import type { NextPage } from "next";
import Head from "next/head";
import React, { ReactElement, useEffect, useRef, useState } from "react";
const Quill = typeof window === "object" ? require("quill") : () => false;
import "quill/dist/quill.snow.css";

type NextPageWithLayout<T> = NextPage<T> & {
  getLayout?: (page: ReactElement) => ReactElement;
};

const NewPostbyQuill: NextPageWithLayout<any> = () => {
  const quillEditorRef = useRef<HTMLDivElement>(null);
  const quillRef = useRef<typeof Quill>(null);

  useEffect(() => {
    //quillRef.current 할당
    quillRef.current = new Quill(quillEditorRef.current, {
      theme: "snow",
      placeholder: "type something...",
    });
  }, []);

  return (
    <div>
      <div>
       
        <div className="Quill_editor">
          <Head>
            <title>Quill Editor</title>
          </Head>
          <main>
            <div ref={quillEditorRef}></div>
          </main>
        </div>

      </div>
    </div>
  );
};
export default NewPostbyQuill;

NewPostbyQuill.getLayout = function getLayout(page: ReactElement) {
  return <div>{page}</div>;
};

Quill이 정상적으로 화면에 나타나고 기능함을 확인했다면 이제 Quill 내부의 data를 받아서 원하는 형태로 만든 후에 서버로 전송하는 코드를 추가해야 한다. 

 

 

아래가 서버에 전송하는 기능까지의 코드이다. 

1. 먼저 다른 <input>태그와 값을 컨토롤하는 state를 지정한 후에 onChange 이벤트로 변화가 실시간으로 반영되도록 세팅한다. 

2. 이후에 맨 아래 <button>태그에서 확인할 수 있듯 onClick 이벤트가 발생함과 동시에 Quill 내부의 데이터를 delta라는 변수에 넣어서, 3. 서버에 전송하는 함수인 postFunction()에 전달해 준다. 

 

// NewPostbyQuill.tsx

"use client";
import type { NextPage } from "next";
import Head from "next/head";
import React, { ReactElement, useEffect, useRef, useState } from "react";
const Quill = typeof window === "object" ? require("quill") : () => false;
import "quill/dist/quill.snow.css";

type NextPageWithLayout<T> = NextPage<T> & {
  getLayout?: (page: ReactElement) => ReactElement;
};

const NewPostbyQuill: NextPageWithLayout<any> = () => {
  const quillEditorRef = useRef<HTMLDivElement>(null);
  const quillRef = useRef<typeof Quill>(null);

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

  function titleChange(event: any) {
    setTitle(event.target.value);
  }

  function postFunction(delta: any) {
    console.log(title);
    console.log(content);
    const contentdata = delta;
    fetch("/api/post/new", { method: "POST", body: JSON.stringify({ title: title, content: contentdata }) });
  }
  useEffect(() => {
    //quillRef.current 할당
    quillRef.current = new Quill(quillEditorRef.current, {
      theme: "snow",
      placeholder: "type something...",
    });
  }, []);

  return (
    <div>
      <div>
        <div>오늘의 노력을 간략하게 소개해주세요.</div>
        <input className="title_box" name="title" placeholder="제목" onChange={titleChange} />

        <div>어떻게 성장할까요?</div>

        <div className="Quill_editor">
          <Head>
            <title>Quill Editor</title>
          </Head>
          <main>
            <div ref={quillEditorRef}></div>
          </main>
        </div>

        <button
          onClick={() => {
            // quillRef로 접근
            var delta = quillRef.current && quillRef.current.getContents();
            postFunction(delta);
          }}
        >
          전송
        </button>
      </div>
    </div>
  );
};
export default NewPostbyQuill;

NewPostbyQuill.getLayout = function getLayout(page: ReactElement) {
  return <div>{page}</div>;
};

 

데이터를 전달받은 서버 코드 

예외처리 해준 후에 최종적으로 전송할 때에 

JSON.stringify() 해서 전송해야한다. 

import { connectDB } from "@/util/database";
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req, res) {
  ...
  
  console.log(req.body);

  console.log(JSON.parse(req.body).title);
  console.log(JSON.parse(req.body).content);
	//JSON.parse() 잊지 말자!


  if (!session) {
    return res.status(400).json("로그인 전에는 글 게시가 불가능합니다.");
  }
  if (req.method == "POST") {
    if (JSON.parse(req.body).title=="") {
      return res.status(500).json("제목(title) 미작성");
    } else if (JSON.parse(req.body).content == "") {
      return res.status(500).json("내용(content) 미작성");
    }
    try {
      let result = db.collection("community").insertOne({
        title: String(JSON.parse(req.body).title),
        content: JSON.stringify(JSON.parse(req.body).content),
        score: 0,
        date: today,
        author: session.user.name,
        email: session.user.email,
        profileurl: session.user.image,
      }); //추천수와 날짜 추가해서 전송
      res.redirect(302, "/community");
    } catch (error) {
      res.status(500).json("서버오류");
    }
  }
}

 

게시글 상세페이지에서 delta 형식을 html 형식으로 변환

마지막으로 서버에서 받아온 delta 형식을 화면에 html로 표시하는 코드이다. 

먼저 quill-delta-to-html 설치

npm install quill-delta-to-html

 

1. delta 형식이 아닌, 즉 Quill 도입 이전에 쓰인 string 형태의 게시물들이 에러를 발생시킬 수 있으므로 if문으로 에러방지

2. QuillDeltaToHtmlConverter로 delta를 html 형식으로 변환시키고 

3.해당 html을 <div dangerouslySetInnerHTML={{ __html: html }} /> 으로 화면에 표시함 

export default async function CommunityDetail(props: ContentProps) {
  ...

  //! 아래는 delta 형식의 content를 html 형식으로 변환하기 위한 코드
  var html = undefined;
  if (result.content != undefined) {
    if (result.content[0] !== "{" || result.content == "") {
      //? 예전에 작성했던 글이 quill의 delta 형식이 아니기 때문에  에러나는 것 방지하기 위해서 분기
      html = undefined;
    } else {
      var QuillDeltaToHtmlConverter = require("quill-delta-to-html").QuillDeltaToHtmlConverter;
      var cfg = {};
      // console.log(JSON.parse(result.content));
      var converter = new QuillDeltaToHtmlConverter(JSON.parse(result.content).ops, cfg);
      html = converter.convert();
    }
  }

  if (result === null) {
    return NotFound();
  } else {
    return (
      <div>
        <h2>노력 자랑 게시판 </h2>
        <article className="post_detail">

			...
          <div className="post_detail_content">
            {html ? <div dangerouslySetInnerHTML={{ __html: html }} /> : result.content}
          </div>
        </article>
        
      </div>
    );
  }
}

 

 


 

정말 여러 게시글에서 자료를 찾아보면서 했기 때문에 글이 정리가 되지 않은 느낌이 있다.

문제는 게시글 쓰는 방식이 달라짐으로 인해서 여러 곳에서 오작동이 발생한다는 것이다. 

에러를 감수하고서라도 위지위그 에디터 기능은 반드시 필요하다고 생각하지만 확인하고 수정해야할 파트가 산더미이다 ㅜㅜ 

 

처음부터 테스트코드를 기반으로 개발했다면 이런 상황에서 훨씬 빠르게 개선할 수 있었겠다는 생각이 든다. 

테스트코드 작성을 공부하고 연습할 필요성을 느끼게 된 좋은 기회였다고 생각한다. 

 

 

 

 

 

참조링크 

 

Quill- Delta

 

Delta - Quill

Delta Deltas are a simple, yet expressive format that can be used to describe Quill’s contents and changes. The format is a strict subset of JSON, is human readable, and easily parsible by machines. Deltas can describe any Quill document, includes all te

quilljs.com

 

quill-delta-to-html

 

 

quill-delta-to-html

Converts Quill's delta ops to HTML. Latest version: 0.12.1, last published: a year ago. Start using quill-delta-to-html in your project by running `npm i quill-delta-to-html`. There are 62 other projects in the npm registry using quill-delta-to-html.

www.npmjs.com

 

 

 

Next.js - 회사 홈페이지 제작 #3 - Quilljs

2022.07.18 - [Next.js] - Next.js - 회사 홈페이지 제작 #2 Next.js - 회사 홈페이지 제작 #2 2022.07.12 - [Next.js] - Next.js - 회사 홈페이지 제작 #1 Next.js - 회사 홈페이지 제작 #1 회사 홈페이지를 만들어야 해서 nex

engschool.tistory.com

 

 

Next.js 프로젝트에 React-Quill(텍스트 에디터) 적용하기

React-Quill 은 리액트 텍스트 에디터로, Destkop/Mobile을 모두 지원하는 몇 안되는 Rich Text Editor 중 하나이다. 줄바꿈, 글꼴, 글자색, 사진, 영상 등을 쉽게 적용할 수 있다.자세한 설명은 공식도큐를 확

velog.io