React WYSIWYG Editor 비교

Genie
13 min readNov 13, 2023

--

프로젝트에서 에디터를 사용할 일이 생겨서 몇 가지 WYSIWYG(위즈윅, What You See Is What You Get) 에디터를 비교해 보려 한다.

필요 기능

  • 한국어 지원
  • UI가 깔끔할 것
  • 폰트, 폰트 사이즈, 폰트 컬러
  • 이미지 업로드, 리사이즈
  • 동영상 업로드(optional)
  • 커스터마이징 하기 편할 것

여러 가지 에디터가 있었지만 그중에 몇 가지를 골라서 테스트해보았다.

1. Toast UI Editor

Toast UI Editor는 NHN에서 지원하는 무료(MIT 라이센스) 위지익 에디터이다.

장점

  • 다른 에디터와 비교했을 때 가장 깔끔한 UI
  • 자세한 공식문서

단점

  • React 18 미지원(2023.11.13 기준)
  • 멀티 이미지 업로드 불가능
  • 폰트 변경 기능 X
  • 유튜브 영상 임베디드 기능 X

UI가 가장 깔끔하고 공식문서도 친절해서 가장 선택할 확률이 높았지만 React 18을 미지원해서 너무 아쉬웠다. 그래도 npm install — force로 설치해서 테스트해 보았다.

// react@18.2.0 next@13.5.4
import dynamic from 'next/dynamic';

const WysiwygEditor = dynamic(() => import('@common/Editor'), {
ssr: false,
});

const ToastUIEditorPage = () => {
return <WysiwygEditor />;
};

export default ToastUIEditorPage;
import '@toast-ui/editor/dist/toastui-editor.css';
import 'tui-color-picker/dist/tui-color-picker.css';
import '@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css';
import '@toast-ui/editor/dist/i18n/ko-kr';

import { Button } from '@mui/material';
import colorSyntax from '@toast-ui/editor-plugin-color-syntax';
import { Editor } from '@toast-ui/react-editor';
import { uploadFile } from 'apis/@common';
import { useRef } from 'react';
import { IMediaItem } from 'types/@common';

const WysiwygEditor = () => {
const editorRef = useRef<Editor | null>(null);

const onSubmit = () => {
console.log(editorRef.current?.getInstance().getHTML());
};

return (
<>
<Editor
placeholder="입력해 주세요."
previewStyle="vertical"
height="600px"
initialEditType="wysiwyg"
usageStatistics={false}
plugins={[colorSyntax]}
useCommandShortcut={false}
language="ko-KR"
ref={editorRef}
hooks={{
addImageBlobHook: async (file, callback) => {
const fileName = file.name;
const fileType = 'image';

const mediaForm: IMediaItem = {
name: fileName,
type: fileType,
feature: 'resource',
formData: file,
previewImageData: URL.createObjectURL(file),
};

const { path } = await uploadFile(mediaForm);
callback(path, fileName);
},
}}
/>
<Button variant="contained" sx={{ mt: 1 }} onClick={onSubmit}>
저장하기
</Button>
</>
);
};

export default WysiwygEditor;

이미지 업로드도 간편하고 공식문서가 친절해서 필요한 플러그인도 쉽게 만들 수 있을 것 같았지만 React18 미지원으로 Pass.

2, Quill

Quill은 비교하는 에디터 중에서 가장 다운로드가 많았고, 커스텀 기능을 넣기에도 편했다.

장점

  • React 18 지원
  • 유튜브 영상 임베디드 기능 O
  • 폰트 변경 기능 O
  • 이미지 리사이즈 기능 O

단점

  • 멀티 이미지 업로드 불가능
  • UI가 살짝 올드
import 'react-quill/dist/quill.snow.css';

import dynamic from 'next/dynamic';

const ReactQuill = dynamic(() => import('@common/Editor/Quill'), {
ssr: false,
});

const QuillPage = () => {
return <ReactQuill />;
};

export default QuillPage;
import { uploadFile } from 'apis/@common';
import { ImageResize } from 'quill-image-resize-module-ts';
import { useRef } from 'react';
import ReactQuill, { Quill } from 'react-quill';
import { IMediaItem } from 'types/@common';

const Font = Quill.import('formats/font');
Font.whitelist = [
'sans-serif',
'arial',
'comic-sans',
'courier-new',
'georgia',
'helvetica',
'lucida',
];
Quill.register(Font, true);
Quill.register('modules/ImageResize', ImageResize);

const QuillEditor = () => {
const quillRef = useRef<ReactQuill | null>(null);

const imageHandler = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
const file: File | null = input.files ? input.files[0] : null;
if (!file) return;
const { name } = file;
const mediaForm: IMediaItem = {
name,
type: 'image',
feature: 'resource',
formData: file,
previewImageData: URL.createObjectURL(file),
};
const editor = quillRef.current?.getEditor();
const range = editor?.getSelection() ?? false;
if (!range) return;
const { path } = await uploadFile(mediaForm);
editor?.insertEmbed(range.index, 'image', path);
editor?.setSelection({
index: range.index + 1,
length: range.length + 1,
});
};
};

const modules = {
toolbar: {
container: [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ font: Font.whitelist }],
[{ align: [] }],
[{ size: ['small', false, 'large', 'huge'] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
'link',
{ indent: '-1' },
{ indent: '+1' },
],
[
{
color: [
'#000000',
'#e60000',
'#ff9900',
'#ffff00',
'#008a00',
'#0066cc',
'custom-color',
],
},
{ background: [] },
],
['image', 'video'],
['clean'],
],
handlers: {
image: imageHandler,
},
},
ImageResize: {
parchment: Quill.import('parchment'),
modules: ['Resize', 'DisplaySize'],
},
};

return <ReactQuill ref={quillRef} modules={modules} />;
};

export default QuillEditor;

3. TinyMCE

TinyMCE는 Evernote, Medium, Atlassian 등의 웹 서비스에서 사용하고 있다. 다양한 기능이 있는 만큼 무겁기는 하지만 UI도 깔끔하고 원하는 기능도 모두 있었다.

장점

  • UI 깔끔
  • 유튜브 영상 임베디드 기능 O
  • 이미지 리사이즈 기능 O

단점

  • 멀티 이미지 업로드 불가능
  • 다른 에디터보다는 무거움
import { Editor } from '@tinymce/tinymce-react';
import { uploadFile } from 'apis/@common';
import React, { useRef } from 'react';
import { IMediaItem } from 'types/@common';

interface BlobInfo {
id: () => string;
name: () => string;
filename: () => string;
blob: () => Blob;
base64: () => string;
blobUri: () => string;
uri: () => string | undefined;
}

const TinymcePage = () => {
const editorRef = useRef(null);

const imageUploadHandler = async (blobInfo: BlobInfo) => {
const fileName = blobInfo.name();
const fileType = 'image';

const mediaForm: IMediaItem = {
name: fileName,
type: fileType,
feature: 'resource',
formData: blobInfo.blob(),
previewImageData: URL.createObjectURL(blobInfo.blob()),
};

const { path } = await uploadFile(mediaForm);
return path;
};

const onEditorChange = (value: string) => {
console.log(value);
};

return (
<Editor
id="tinyEditor"
apiKey="apiKey"
init={{
plugins:
'tinycomments mentions anchor autolink charmap codesample emoticons image link lists media searchreplace table visualblocks wordcount checklist mediaembed casechange export formatpainter pageembed permanentpen footnotes advtemplate advtable advcode editimage tableofcontents mergetags powerpaste tinymcespellchecker autocorrect a11ychecker typography inlinecss',
toolbar:
'undo redo | blocks fontfamily fontsize forecolor | bold italic underline strikethrough | link image media table mergetags | align lineheight | tinycomments | checklist numlist bullist indent outdent | emoticons charmap | removeformat',
tinycomments_mode: 'embedded',
automatic_uploads: true,
file_browser_callback_types: 'image',
images_upload_handler: imageUploadHandler,
language: 'ko_KR',
min_height: 700,
}}
initialValue="Welcome to TinyMCE!"
onEditorChange={onEditorChange}
ref={editorRef}
/>
);
};

export default TinymcePage;

이미지 업로드를 하면 사이즈를 확인하고 확인 버튼을 한 번 더 눌러야 하는 번거로움이 있었다. 추후에 이 에디터를 사용하게 된다면 메뉴바는 없애고 툴바만 사용할 것 같다.

지금까지 살펴본 에디터 중에서는 Quill 에디터가 프로젝트에 가장 적합했다. 필요한 기능에 따라 에디터 선택이 달라질 수 있으니 잘 비교해 보면 좋을 것 같다.

--

--