게시글 에디터 정리
게시글 작성 에디터 완벽 가이드: WYSIWYG부터 마크다운까지
서론
블로그, CMS, 위키 등 콘텐츠 관리 시스템에서 사용자에게 편리한 게시글 작성 경험을 제공하는 것은 중요합니다. 다양한 에디터 타입이 있으며, 각각의 특징과 장단점을 이해하여 프로젝트에 적합한 에디터를 선택하는 것이 핵심입니다. 이 글에서는 게시글 작성 에디터의 종류와 주요 라이브러리들을 다루겠습니다.
1. 에디터 타입 분류
1.1 WYSIWYG 에디터
WYSIWYG(What You See Is What You Get) 에디터는 사용자가 보는 화면이 실제 출력 결과와 동일한 에디터입니다.
특징:
- 시각적 편집 인터페이스
- HTML 출력
- 비기술자도 쉽게 사용 가능
- 실시간 미리보기
장점:
- 직관적인 사용자 경험
- 빠른 콘텐츠 작성
- 서식 적용이 쉬움
단점:
- 불필요한 HTML 태그 생성 가능
- 일관성 없는 마크업
- 접근성 문제 가능
1.2 마크다운 에디터
마크다운 에디터는 마크다운 문법을 사용하여 텍스트를 작성하는 에디터입니다.
특징:
- 간단한 문법
- 깔끔한 텍스트 포맷
- 버전 관리에 유리
- 개발자 친화적
장점:
- 가벼운 마크업
- 읽기 쉬운 소스
- 일관된 포맷
단점:
- 학습 곡선 존재
- 복잡한 레이아웃 제한
1.3 블록 에디터
블록 에디터는 콘텐츠를 블록 단위로 구성하는 에디터입니다. WordPress Gutenberg가 대표적입니다.
특징:
- 모듈화된 콘텐츠 구조
- 드래그 앤 드롭
- 재사용 가능한 블록
- 구조화된 데이터
장점:
- 유연한 레이아웃
- 재사용성
- 구조화된 콘텐츠
단점:
- 복잡한 구조
- 학습 필요
2. 주요 WYSIWYG 에디터 라이브러리
2.1 Quill
Quill은 모던하고 강력한 리치 텍스트 에디터입니다.
특징:
- 가벼운 번들 크기
- 모듈화된 구조
- 커스터마이징 용이
- 확장 가능
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
],
},
});
2.2 TinyMCE
TinyMCE는 가장 널리 사용되는 WYSIWYG 에디터 중 하나입니다.
특징:
- 풍부한 기능
- 플러그인 생태계
- 접근성 지원
- 엔터프라이즈 지원
import tinymce from 'tinymce';
import 'tinymce/themes/silver';
import 'tinymce/plugins/link';
import 'tinymce/plugins/image';
tinymce.init({
selector: '#editor',
plugins: 'link image code',
toolbar: 'bold italic | link image | code',
});
2.3 CKEditor
CKEditor는 강력한 기능을 제공하는 에디터입니다.
특징:
- 다양한 에디션 제공
- 강력한 문서 편집 기능
- 협업 기능
- 클라우드 서비스
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
console.log('Editor initialized', editor);
})
.catch(error => {
console.error(error);
});
3. 마크다운 에디터
3.1 react-markdown
React에서 마크다운을 렌더링하는 라이브러리입니다.
import ReactMarkdown from 'react-markdown';
function MarkdownViewer({ content }) {
return <ReactMarkdown>{content}</ReactMarkdown>;
}
3.2 marked
마크다운을 HTML로 변환하는 라이브러리입니다.
import { marked } from 'marked';
const html = marked('# Hello World\n\nThis is **bold** text.');
3.3 CodeMirror / Monaco Editor
코드 에디터 기반의 마크다운 에디터입니다.
import { EditorView } from '@codemirror/view';
import { EditorState } from '@codemirror/state';
const editor = new EditorView({
state: EditorState.create({
doc: '# Markdown content',
}),
parent: document.body,
});
4. 블록 에디터
4.1 Editor.js
Editor.js는 블록 스타일의 에디터입니다.
import EditorJS from '@editorjs/editorjs';
import Header from '@editorjs/header';
import List from '@editorjs/list';
const editor = new EditorJS({
holder: 'editor',
tools: {
header: Header,
list: List,
},
});
4.2 Slate
Slate는 완전히 커스터마이징 가능한 리치 텍스트 에디터 프레임워크입니다.
import { createEditor } from 'slate';
import { Slate, Editable, withReact } from 'slate-react';
const editor = withReact(createEditor());
function MyEditor() {
const [value, setValue] = useState([
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]);
return (
<Slate editor={editor} value={value} onChange={setValue}>
<Editable />
</Slate>
);
}
5. Next.js에서 에디터 구현
5.1 React Quill 통합
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const ReactQuill = dynamic(() => import('react-quill'), { ssr: false });
import 'react-quill/dist/quill.snow.css';
export default function PostEditor() {
const [content, setContent] = useState('');
const modules = {
toolbar: [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
],
};
return (
<ReactQuill
theme="snow"
value={content}
onChange={setContent}
modules={modules}
/>
);
}
5.2 마크다운 에디터 구현
'use client';
import { useState } from 'react';
import ReactMarkdown from 'react-markdown';
export default function MarkdownEditor() {
const [markdown, setMarkdown] = useState('');
return (
<div className="grid grid-cols-2 gap-4">
<div>
<textarea
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
className="w-full h-96 p-4 border"
placeholder="마크다운을 입력하세요..."
/>
</div>
<div className="border p-4">
<ReactMarkdown>{markdown}</ReactMarkdown>
</div>
</div>
);
}
6. 이미지 업로드 처리
6.1 이미지 핸들러 구현
const imageHandler = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
const file = input.files?.[0];
if (!file) return;
const formData = new FormData();
formData.append('image', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const data = await response.json();
const quill = quillRef.current.getEditor();
const range = quill.getSelection();
quill.insertEmbed(range.index, 'image', data.url);
};
};
7. 에디터 선택 가이드
7.1 사용자 타겟에 따른 선택
비기술 사용자:
- WYSIWYG 에디터 (Quill, TinyMCE)
- 시각적 편집 인터페이스
- 간단한 학습 곡선
기술 사용자:
- 마크다운 에디터
- 빠른 작성 속도
- 버전 관리 용이
복잡한 콘텐츠:
- 블록 에디터 (Editor.js, Slate)
- 구조화된 레이아웃
- 재사용 가능한 컴포넌트
7.2 프로젝트 요구사항
간단한 블로그:
- 마크다운 에디터
- 가벼운 구현
- 빠른 개발
CMS 시스템:
- WYSIWYG 에디터
- 사용자 친화적
- 다양한 서식 지원
복잡한 콘텐츠 관리:
- 블록 에디터
- 구조화된 데이터
- 확장 가능성
8. 성능 최적화
8.1 동적 임포트
const Editor = dynamic(() => import('./EditorComponent'), {
ssr: false,
loading: () => <div>에디터 로딩 중...</div>,
});
8.2 콘텐츠 지연 로딩
const [isEditorReady, setIsEditorReady] = useState(false);
useEffect(() => {
// 에디터 초기화
setIsEditorReady(true);
}, []);
9. 접근성 고려사항
9.1 키보드 네비게이션
- Tab 키로 툴바 이동
- Enter로 새 줄 생성
- Esc로 포커스 해제
9.2 스크린 리더 지원
- 적절한 ARIA 레이블
- 역할(role) 속성
- 라이브 영역(live region)
10. 결론
게시글 작성 에디터는 사용자 경험에 직접적인 영향을 미치는 중요한 컴포넌트입니다. 프로젝트의 요구사항과 사용자 타겟에 따라 적절한 에디터를 선택하는 것이 핵심입니다.
주요 포인트:
- WYSIWYG: 비기술 사용자에게 적합
- 마크다운: 개발자와 기술 사용자에게 적합
- 블록 에디터: 복잡한 콘텐츠 구조에 적합
- 성능: 동적 임포트와 지연 로딩 활용
- 접근성: 키보드 네비게이션과 스크린 리더 지원
각 에디터 타입은 고유한 장단점이 있으므로, 프로젝트의 특성과 사용자 요구사항을 고려하여 선택해야 합니다.