React-Quill 은 리액트 텍스트 에디터로, Destkop/Mobile을 모두 지원하는 몇 안되는 Rich Text Editor 중 하나이다. 줄바꿈, 글꼴, 글자색, 사진, 영상 등을 쉽게 적용할 수 있다.
자세한 설명은 공식도큐를 확인하면 좋다:
yarn add react-quill
import 'react-quill/dist/quill.snow.css';
Quill은 ssr 지원이 되지 않기 때문에 단순히 import ReactQuill from react-quill
처럼 static import을 하면 에러가 뜬다.
document is not defined
SSR?
SSR(Server side rendering)은 화면에서 보여줄 페이지의 View를 서버 단에서 렌더링을 한 후, 클라이언트에게 제공한다. View를 미리 렌더링해서 사용자에게 웹 화면으로 보여주고, 이후에 웹 페이지를 동작하게 하거나 구성하는 리소스들을 로드한다. 완벽하게 기능이 모두 갖춰진 상태로 로드하는게 아니라, 최소한의 UI만 먼저 제공 하여 로딩 속도가 빠른 것 처럼 느끼게 해주기 위함이다.
document is not defined 에러가 나는 이유
document는 모든 view 및 기능이 로드된 시점에 정의가 되는데, document가 정의되기 전에 react-quill이 로드 되고, 정의되지 않은 document를 조작하려고 해서 에러가 발생하는 것이다.
즉,
document 정의 -> react-quill 로드 -> react-quill 이 document 조작
의 순서가 되어야 하는데,
react-quill 로드 -> react-quill 이 document 조작 -> document 정의
순서가 되니, 에러가 발생되는 것이다.
Next.js는 JavaScript 용 ES2020 dynamic import() 을 지원하는데, 모듈을 동적으로 import할 수 있도록 한다. dynamic import을 하면 Quill Editor를 서버 측 모듈에 포함하지 않고, 런타임시에 모듈을 import할 수 있다.
import dynamic from 'next/dynamic'
Dynamic Import?
대부분의 코드들은 사용자가 보는 첫 페이지에는 필요하지 않다. 따라서, 첫 페이지 진입시에 필요한 최소한의 코드만 다운 받고, 사용자가 특정 페이지나 위치에 도달할 때마다 코드를 로드 한다면, 첫 페이지의 초기 성능을 올릴 수 있다.
이런 방식을 lazy-load 게으른 로딩이라고 한다.
Dynamic Import
를 사용하면, 런타임시에 필요한module
을import
할 수 있다.
{ssr : false}
로 세팅하고, 로드되는 동안의 상태를 렌더링할 수 있든 loading 함수도 설정할 수 있다. 아래와 같이 작성하여 만든 quill 컴포넌트를 원하는 페이지에 임포트해서 사용하면 된다.
const QuillWrapper = dynamic(() => import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
*댓글에서 알려주셔서 수정했습니다. 기존에는 바로
dynamic(import("react-quill")
로 임포드 했었는데, build 에러가 떠서dynamic(() => import('react-quill')
로 변경이 필요하다고 하십니다 :)
그리고 만든 QuillWrapper 컴포넌트를 페이지에서 사용할 때는 아래와 같이 기본 theme 값을 주고 사용하면 된다.
export default function Home() {
return <QuillWrapper theme="snow" />
}
아래와 같이 기본 텍스트 에디터가 뜨면 성공이다.
기본 feature 이외에도 이미지, 비디오와 같은 기능을 추가할 수 있다.
아래는 Use Quill as a rich text editor in next.js 에서 사용한 예제이다:
import dynamic from 'next/dynamic'
const QuillNoSSRWrapper = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
],
['link', 'image', 'video'],
['clean'],
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
}
/*
* Quill editor formats
* See https://quilljs.com/docs/formats/
*/
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'strike',
'blockquote',
'list',
'bullet',
'indent',
'link',
'image',
'video',
]
export default function Home() {
return <QuillNoSSRWrapper modules={modules} formats={formats} theme="snow" />
}
Quill 컴포넌트에 modules와 format를 적용하면 헤더, 폰트, 링크, 이미지, 비디오 등 기능이 추가된 것을 볼 수 있다.
<QuillWrapper
theme={'snow'}
id={'description'}
placeholder={'설명을 입력해주세요'}
value={values.description}
modules={modules}
formats={formats}
onChange={(event) => setFieldValue('description', event)}
/>
아래 링크에서 Quill 툴바 설정에 대해서 더 자세히 볼 수 있다:
https://quilljs.com/docs/modules/toolbar/
Quill 포맷 참고:
https://quilljs.com/docs/formats/
혹시 auto focusing도 해결하셨는지 조심스레 여쭙습니다.
아무리해도 잘 안되어... ㅜㅜ;