Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Next.js

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

방피터 2022. 7. 22. 15:06

 

2022.07.18 - [Next.js] - Next.js - 회사 홈페이지 제작 #2

 

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

2022.07.12 - [Next.js] - Next.js - 회사 홈페이지 제작 #1 Next.js - 회사 홈페이지 제작 #1 회사 홈페이지를 만들어야 해서 next.js로 해보려고. 기업 홈페이지처럼 정보만 나열하는 페이지를 보통 static page..

engschool.tistory.com

 

회사 공지사항을 올리는 게시판이 필요해. 지금 생각나는 구현 방법 중 쉬운 버전은 단순하게 md file로 작성된 게시물을 리스트로 보여주고 클릭하면 본문을 보여주는 방식. md file로 문서 작성하고 그걸 다시 next.js 프로젝트에 포함해서 배포하고 뭐 그런식이겠지? 이런 방식은 나중에 무지하게 귀찮을 것 같아.

약간 난이도가 있는 버전은 로그인 기능을 넣고 권한이 있는 사람이 웹 페이지에서 텍스트 에디터를 사용해서 공지를 작성하고 데이터베이스에 저장할 수 있어야겠지. 그리고 데이터 베이스에서 리스트를 읽어올 수 있는 페이지가 필요하고, 일반 유저가 그 리스트 중 하나를 클릭하면 내용을 읽어와서 화면에 보여줄 수 있는 페이지도 필요하겠지.

이왕 하는 거 약간 난이도가 있는 버전으로 구현해보기로 결정했어. aws amplify에도 익숙해져야 하고 말이지. 먼저 text editor를 구현해보자구. 보통 rich text editor 라고 부르는데 WYSIWYG(what you see is what you get) 편집기라고도 하고.. 내가 보기에는 방식에 가깝지만. 암튼 rich text editor js library는 많은 종류가 있는데 난 구글에서 공짜 중에 가장 위에 검색되는 거 사용할거야. 그러니까 quilljs라는 게 보이네. 고민하지말고 일단 가보자구.

rich text editor free 검색 결과

quilljs 홈페이지 👇👇

https://quilljs.com/

 

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

보는 대로 대문에 공짜이고 오픈소스인 WYSIWYG editor라고 되어 있으니가 이거 쓰자구. 좋은지 나쁜지 검색하지말고 그냥 써보는 것도 좋은 방법이야. 너무 단물만 빨려고 하면 ㅋㅋ 이빨 썩어 ㅋ 낙오도 하고 그래야지 ㅋ 

quill은 꽁짜고 open source이고 wysiwyg editor 임!

자 documentation으로 가보자구. 설치 방법이랑 사용법이랑 대충 한번 훑어야지. Quickstart👇👇에는 script태그 안에서 CDN으로 소스 얻어와서 div id로 quill 에디터를 초기화 시키네. 우리는 nextjs 안에서 사용할 거니까 node module 설치해서 하는게 좋겠지?

quilljs Quickstart

난 yarn 만 사용하니까 아래와 같이 quill이랑 types 추가.

yarn add quill@1.3.7 @types/quill

그리고 pages폴더에 editor.tsx 파일 생성하고 아래👇👇 와 같이 코드 추가.

import type { NextPage } from "next";
import Head from "next/head";
import { Container } from "@mui/material";
import tw, { styled } from "twin.macro";
import React, { ReactElement, useEffect, useRef, useState } from "react";
import Layout from "../components/Layout";
import Quill from "quill";
import "quill/dist/quill.snow.css";

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

const MainContainer = styled(Container)(() => [
  tw`relative text-center min-h-full`,
]);

const editor: NextPageWithLayout<any> = () => {
  const quillEditorRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    if (quillEditorRef.current) {
      const quill = new Quill(quillEditorRef.current, {
        theme: "snow",
        placeholder: "type something...",
      });
    }
  }, [quillEditorRef]);

  return (
    <MainContainer>
      <Head>
        <title>Editor</title>
      </Head>
      <main>
        <div ref={quillEditorRef}></div>
      </main>
    </MainContainer>
  );
};
export default editor;

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

하면 에러가 발생해. document가 defined 되어 있지 않다고 그러네. 이건 window 객체가 생성이 안되서 그 안에 있는 document 객체도 undefined 이기 때문에 발생한다고 하는데... 잘 이해가 안되지만 quill은 import할 때 window객체가 필요한가봐... 왤까???? 아는 사람 좀 알려줘.

document is not defined

그래서 아래와 같이 window object가 잘 있으면 require로 import. 아니면 false.

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

그리고 나면 아래 👇 처럼 quill editor가 잘(?) 나오지 ㅎㅎㅎㅎ 잘은 아닌거 같네 그치? ㅋㅋ quill 헤더가 두개 나오네? ㅋㅋㅋ 이건 또 뭐여? quill 초기화 시키는 useEffect가 두번 실행되네? react strict mode에서 발생하는데 괜히 설정은 바꾸기 싫으니까 useState를 하나 더 추가해서 해결하자고. (next.config.js파일에서 strict mode를 false로 변경해도 정상적으로 동작해)

quill editor 로드 하지만 헤더가 두개다...

아래처럼 말이지.👇 그다지 복잡하지 않아. quillState를 생성 -> quillEditorRef이 정상적으로 할당되면 quillState를 true로 변경. 그리고 다른 useEffect안에서 quillState가 true면 그 때 비로소 quill 에디터 초기화.

import type { NextPage } from "next";
import Head from "next/head";
import { Container } from "@mui/material";
import tw, { styled } from "twin.macro";
import React, { ReactElement, useEffect, useRef, useState } from "react";
import Layout from "../components/Layout";
import "quill/dist/quill.snow.css";

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

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

const MainContainer = styled(Container)(() => [
  tw`relative text-center min-h-full`,
]);

const editor: NextPageWithLayout<any> = () => {
  const quillEditorRef = useRef<HTMLDivElement>(null);
  const [quillState, setQuillState] = useState<boolean>(false);
  useEffect(() => {
    if (quillEditorRef.current) {
      setQuillState(true);
    }
  }, [quillEditorRef]);

  useEffect(() => {
    if (quillState) {
      const quill = new Quill(quillEditorRef.current, {
        theme: "snow",
        placeholder: "type something...",
      });
    }
  }, [quillState]);

  return (
    <MainContainer>
      <Head>
        <title>Editor</title>
      </Head>
      <main>
        <div ref={quillEditorRef}></div>
      </main>
    </MainContainer>
  );
};
export default editor;

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

이렇게 하고 나면 아래와 같이 헤더가 한개인 quill 에디터가 뿅하고 나와. 

정상적으로 로드된 quill 에디터

이제 quill로 작성한 내용을 불러와서 DB에 넣어야겠지? 그리고 저장된 객체를 다시 화면에 뿌릴 수도 있어야 해. quill에 작성된 내용을 가져오기 위해서는 getContents() 메서드를 이용해. 저장 버튼을 누르면 const content = quill.getContents() 이런식으로 읽어와야 하지. 하지만 기존에 useEffect 안에서 const quill = new Quill(... 이런식으로 할당했기 때문에 다른곳에서 quill 객체에 접근할 수가 없지. 외부에서도 quill 에 접근할 수 있도록  useRef로 quillRef를 만들고 외부에서는 quillRef를 통해서 접근할 수 있도록 코드를 변경해야 해.

...
//quill object를 외부에서 사용하기 위해 useRef
const quillRef = useRef<typeof Quill>(null);
...
useEffect(() => {
    if (quillState) {
      //quillRef.current 할당
      quillRef.current = new Quill(quillEditorRef.current, {
        theme: "snow",
        placeholder: "type something...",
      });
    }
  }, [quillState]);
...
...
		<Button
          onClick={() => {
            //quillRef로 접근.
            var delta = quillRef.current && quillRef.current.getContents();
            console.log(delta);
          }}
        >
          저장
        </Button>
        ...

테스트를 해보면 아래와 같이 getContents()가 잘 동작하는 걸 확인할 수 있어. getContents()는 delta형식의 객체를 반환하지. 👉👉 https://quilljs.com/docs/delta/

delta 타입 객체를 잘 DB에 저장하면 되겠다.

뭐 문서 수정하고 작성하고 하는 건 이거로 한다 치는데... 유저들한테 보여주는 건 어떻게 해야하지? -_-;; 저 delta 를 html로 변환할 수 있어야 하는데 ㅎㅎㅎ delta to html 라이브러리들은 업데이트가 옛날이라 못쓰겠고 ㅎ 뭐 안되는게 어딨어? ㅎㅎ 고민해 봅시다.

안녕!

반응형