Dee Editor 튜토리얼 v2.4.1.0
바로 활용할 수 있는 실전 예제·튜토리얼 모음 (라이브 데모 포함)
📘 이 문서는 "튜토리얼"입니다. 실행 가능한 예제와 통합 패턴을 다룹니다.
모든 옵션·메서드·이벤트·타입의 전체 명세📑 공식 API 레퍼런스를 참조하세요. (레퍼런스 = 정확한 단일 출처, 본 가이드 = 사용법 중심)
0. 패키지 / 소스 파일 구조
배포 ZIP을 풀면 다음과 같은 구조가 생성됩니다. 다른 개발자가 바로 활용하거나 수정·확장할 수 있도록 소스와 빌드 산출물이 모두 포함됩니다.
배포 산출물 (dist/)
dist/
├── webeditor.js              // UMD 번들 (가독성 유지)
├── webeditor.min.js          // UMD 번들 (terser minified)
├── webeditor.esm.js          // ES Module 빌드
├── webeditor.css             // 전체 스타일
├── webeditor.min.css         // 압축 스타일
├── webeditor.d.ts            // TypeScript 타입 선언
└── plugins/                  // 별도 로드 플러그인
    ├── wordimport-native.js      // MS Word 가져오기 (Native engine)
    └── wordimport-native.min.js
소스 (src/)
src/
├── editor.js                 // 코어 Editor 클래스 + 옵션/이벤트 시스템
├── editor.css                // 툴바·본문·모달·상태바 등 전체 CSS
├── toolbar.js                // 툴바 렌더링/디스패치
├── menubar.js                // 7개 그룹 드롭다운 메뉴바 + About 다이얼로그
├── dom-api.js                // WebEditorAPIModel (DOM 조회·수정 체이너블 API)
├── license/                  // 라이선스 검증 (HMAC-SHA256, 도메인 매칭)
│   ├── manager.js
│   ├── validator.js
│   ├── domain-matcher.js
│   ├── i18n.js
│   └── evaluation-modal.js
└── plugins/
    ├── format.js             // Bold/Italic/색상/크기/정렬/목록
    ├── image.js              // 붙여넣기 + 드래그앤드롭 + 리사이즈
    ├── table.js              // 격자 피커 + 셀 병합 + 음영 + 코너 핸들 리사이즈
    ├── link.js               // 하이퍼링크 다이얼로그
    ├── emoji.js              // 8개 카테고리 이모지 팔레트
    ├── video.js              // YouTube/Vimeo 임베드
    ├── sourceview.js         // HTML 소스 보기/편집 탭
    ├── insert.js             // 가로줄·특수문자·페이지 나누기
    ├── find.js               // 찾기/바꾸기
    └── wordimport/           // MS Word 가져오기 (Phase 2.3 NEW)
        ├── index.js          // 플러그인 오케스트레이터
        ├── core/
        │   ├── DocxReader.js         // ZIP 해제 (DecompressionStream)
        │   └── HtmlNormalizer.js     // 변환 결과 살균
        ├── engines/
        │   ├── EngineRegistry.js     // 엔진 등록·선택
        │   └── NativeEngine.js       // OOXML→HTML 변환 엔진
        └── ui/
            ├── ImportDialog.js       // 옵션 선택 모달
            └── ProgressDialog.js     // 진행 인디케이터
데모 페이지 (demo/) · 자산 (assets/) · 로고 (img/)
demo/
├── sdk-guide.html            // 본 문서 — 통합 가이드
├── board.html                // 게시판 목록
├── write.html                // 게시글 작성 (Word 가져오기 통합)
├── view.html                 // 게시글 보기 (XSS 새니타이저 포함)
├── dom-api.html              // DOM API 인터랙티브 데모
├── menubar.html              // 메뉴바·UI 컨트롤 데모
└── wordimport.html           // MS Word 가져오기 데모

assets/
└── icons.svg                 // SVG 스프라이트 (60+ symbols)

img/
├── dee1.PNG                  // 밝은 배경용 로고
├── dee2.PNG                  // 어두운 배경용 로고
└── dee_gray.png              // 무채색 로고
빌드·패키징 스크립트
build.js                      // 코어 + 플러그인 별도 번들 + 미니파이
make-package.js               // 고객용/사내용 ZIP 생성
package.json                  // npm scripts: build / build:watch
📄 MS Word 가져오기 플러그인 (NEW) — 별도 번들 구조

Word 가져오기는 코어 번들과 분리된 외부 플러그인으로 설계되어 있습니다. 에디션(Standard / Premium) 별로 다른 변환 엔진을 선택할 수 있도록 EngineRegistry 패턴을 사용합니다. 같은 메뉴/툴바 UI에서 어떤 엔진이 등록되어 있든 자동으로 가장 우선순위 높은 엔진을 사용합니다.

src/plugins/wordimport/
├── index.js                            // WordImportPlugin — 오케스트레이터//   · 메뉴/툴바/드래그앤드롭 진입//   · editor.importWordFile(file, options) API 노출//   · 자기 등록: WebEditor.registerPlugin('wordimport', ...)
├── core/
│   ├── DocxReader.js                   // ZIP 해제 (DecompressionStream 'deflate-raw')
│   │                                   //   · EOCD/Central Directory 파싱
│   │                                   //   · 엔트리별 STORE/DEFLATE 해제 → Map<path, Uint8Array>
│   └── HtmlNormalizer.js               // 변환 결과 살균 (script/on*/javascript: 제거)
├── engines/
│   ├── EngineRegistry.js               // 엔진 등록·선택 (priority 기반)
│   │                                   //   · register / all / best / byName
│   └── NativeEngine.js                 // Standard 에디션 변환 엔진 (의존성 0)//   · OOXML(w:p/w:r/w:tbl/...) → HTML//   · 단락·서식·표(tblGrid/음영/병합)·목록·링크·이미지//   · EMU → px 정확 변환 (1 inch = 914400 EMU = 96 px)
└── ui/
    ├── ImportDialog.js                 // 옵션 모달 (엔진/삽입모드/이미지/로그)
    └── ProgressDialog.js               // 변환 진행 스피너

dist/plugins/                           // 빌드 산출
├── wordimport-native.js                // 49 KB — 자기등록 IIFE 번들
└── wordimport-native.min.js            // 21 KB — terser minified
설치·사용 (3 라인):
<!-- 1. 코어 로드 -->
<script src="dist/webeditor.min.js"></script>
<!-- 2. Word 가져오기 플러그인 로드 (별도 파일) -->
<script src="dist/plugins/wordimport-native.min.js"></script>
<!-- 3. 에디터 옵션에 'wordimport' 추가 -->
<script>
const editor = new WebEditor('#editor', {
  menubar: true,
  plugins: ['format', 'image', 'table', 'link', 'wordimport'],
});
</script>
3가지 진입점: ① 파일 메뉴 → "MS Word 가져오기…"   ② 툴바의 워드 아이콘(W)   ③ 본문에 .docx 파일 드래그앤드롭
에디션 전략: Standard 에디션은 NativeEngine만 포함 (외부 의존성 0, 약 21KB). Premium 에디션은 추후 MammothEngine.js를 같은 폴더 구조로 추가하면 EngineRegistry가 자동으로 우선순위(priority=100)에 따라 선택합니다. UI/메뉴/사용자 코드는 변경할 필요가 없습니다.
지원 요구사항: Chrome 80+ · Edge 80+ · Firefox 113+ · Safari 16.4+ (브라우저 내장 DecompressionStream API 필수)
1. 기본 사용법
가장 간단한 형태. <script> 하나와 new WebEditor()만으로 사용합니다.
HTML + JS
<!-- 1. CSS 로드 -->
<link rel="stylesheet" href="dist/webeditor.min.css">

<!-- 2. 컨테이너 -->
<div id="editor"></div>

<!-- 3. JS 로드 + 초기화 -->
<script src="dist/webeditor.min.js"></script>
<script>
  const editor = new WebEditor('#editor');
</script>
라이브 데모
← getHTML() 버튼을 누르면 HTML이 여기 표시됩니다
2. 옵션 (Options)
자주 쓰는 옵션의 사용 예제입니다. 전체 옵션 목록(약 30개)·기본값·타입📑 API 레퍼런스 §2 초기화 옵션을 참조하세요.
이벤트 옵션 예제
const editor = new WebEditor('#editor', {
  height:      '500px',
  placeholder: '게시판 내용을 입력하세요.',
  onChange: (html) => {
    document.getElementById('char-count').textContent =
      editor.body.innerText.replace(/\s/g, '').length + '자';
  },
  onFocus: ()  => console.log('편집 시작'),
  onBlur:  ()  => console.log('편집 완료'),
});
글자수: 0 포커스: - 마지막 변경: -
3. 플러그인 선택
필요한 플러그인만 골라 가볍게 사용할 수 있습니다.
const editor = new WebEditor('#editor', {
  plugins: ['format', 'link'],  // 서식 + 링크만
});
const editor = new WebEditor('#editor', {
  plugins: ['format', 'image', 'link', 'emoji', 'insert'],
});
const editor = new WebEditor('#editor'); // 기본 = 전체
4. 툴바 커스터마이즈
toolbar 옵션으로 버튼 배치를 완전히 제어합니다. '|'는 구분선입니다.
const editor = new WebEditor('#editor', {
  toolbar: [
    // 1행: 서식 핵심만
    ['bold', 'italic', 'underline', 'strikeThrough', '|',
     'fontColor', 'highlightColor', '|', 'removeFormat'],
    // 2행: 레이아웃 + 삽입
    ['justifyLeft', 'justifyCenter', 'justifyRight', '|',
     'insertImage', 'insertLink', 'insertTable', '|',
     'undo', 'redo'],
  ],
});
5. 이미지 서버 업로드
uploadHandler를 지정하면 이미지를 서버에 업로드한 뒤 URL로 삽입합니다. 미지정 시 base64 Data URL로 삽입됩니다.
const editor = new WebEditor('#editor', {
  uploadHandler: async (file) => {
    const form = new FormData();
    form.append('image', file);

    const res = await fetch('/api/upload', { method: 'POST', body: form });
    const { url } = await res.json();
    return url;  // 최종 URL 반환 필수
  },
});
이미지 삽입 방법 3가지: ① 툴바 버튼 클릭 → 파일 선택 ② Ctrl+V 붙여넣기 ③ 파일 드래그앤드롭
6. 커스텀 플러그인 작성
3개 메서드(init, execute, queryState)를 구현하면 됩니다.
// 글자수 제한 플러그인 예제
class CharLimitPlugin {
  constructor(editor, limit = 1000) {
    this.editor = editor;
    this.limit  = limit;
  }

  init() {
    this.editor.on('change', () => {
      const chars = this.editor.body.innerText.replace(/\s/g, '').length;
      if (chars > this.limit) {
        alert(`글자수 초과! 현재 ${chars}자 / 최대 ${this.limit}자`);
      }
    });
  }

  execute(command) {}   // 툴바 버튼 없으면 비워도 됨
  queryState() { return {}; }
}

// 사용
const editor = new WebEditor('#editor', { plugins: ['format'] });
const limiter = new CharLimitPlugin(editor, 500);
limiter.init();
editor.plugins.push(limiter);
글자수: 0 / 500자
7. 폼(Form) 연동
게시판 글쓰기 폼에 에디터를 연동하는 패턴입니다.
// 폼 제출 시 hidden input에 HTML 복사
const editor = new WebEditor('#editor');
const form = document.querySelector('#post-form');

form.addEventListener('submit', (e) => {
  const html = editor.getHTML();
  if (!html.trim() || html === '<br>') {
    e.preventDefault();
    alert('내용을 입력해주세요.');
    editor.focus();
    return;
  }
  document.getElementById('content-input').value = html;
});
8. DOM API
에디터 본문 내 DOM 요소를 ID·선택자로 조회하고, 속성·스타일·내용을 안전하게 입력/추출하는 API입니다. Synap Editor의 DOM API와 유사한 인터페이스를 제공합니다.
① 진입점 (Entry Points)
// ID로 요소 모델 획득
const table = editor.getAPIModelById('mytable');

// CSS 선택자로 조회
const firstP = editor.querySelector('p');
const allImgs = editor.querySelectorAll('img');

// 본문 루트
const root = editor.getRoot();
// or editor.getBody()

// 옵션 — 변경 이벤트 억제 (일괄 작업용)
const p = editor.querySelector('p', { skipRendering: true });
② 데이터 추출 (Extract)
const el = editor.getAPIModelById('title');

el.getText();          // "제목 텍스트"
el.getHTML();          // "<strong>제목</strong>"
el.getTagName();       // "h2"
el.getId();            // "title"
el.getAttribute('class');
el.getStyle();         // "color:red; font-size:14pt"
el.getCSS('color');    // "red"
el.getRect();          // DOMRect {x, y, width, height}
el.getWidth();         // offsetWidth
el.getNode();          // 원시 DOM Element (escape hatch)
③ 데이터 입력 (Set / Modify)
el.setText('새 텍스트');
el.setHTML('<b>HTML</b>');
el.setStyle('color:red; font-weight:bold');
el.setCSS('color', 'blue');
el.setCSS({ color: 'red', fontSize: '14pt' });

el.setAttribute('data-id', '123');
el.removeAttribute('data-id');

el.addClass('highlight');
el.removeClass('highlight');
el.toggleClass('active');
el.hasClass('active');    // true / false

// 모든 메서드는 자기 자신을 반환 → 체이닝 가능
el.setText('Hello').addClass('note').setStyle('color:blue');
④ 탐색 / 삽입 / 제거
// 탐색
el.parent();                          // APIModel | null
el.children();                        // APIModel[]
el.nextSibling(); el.prevSibling();
el.find('td');                       // 하위 검색 (배열)
el.findOne('td');                    // 하위 첫 매치
el.closest('table');                  // 조상 검색 (선택자 또는 함수)
el.closest(m => m.hasClass('foo'));

// 삽입 — 문자열 / Node / APIModel 모두 허용
el.append('<p>끝에</p>');
el.prepend('<p>앞에</p>');
el.insertBefore('<hr>');
el.insertAfter('<hr>');

// 제거
el.empty();   // 자식 노드 모두 제거
el.remove();  // 자기 자신 제거
⑤ 표 전용 헬퍼
const tbl = editor.getAPIModelById('mytable');

tbl.insertRow(1);    // 인덱스 1번 위에 새 행 삽입
tbl.insertCol(2);    // 인덱스 2번 왼쪽에 새 열 삽입
tbl.deleteRow(0);    // 0번 행 삭제
tbl.deleteCol();     // 마지막 열 삭제
tbl.setWidth(400);
tbl.setHeight('200px');
⑥ 일괄 처리 — skipRendering + render()
변경 이벤트(change)는 매 작업마다 발생합니다. 다수 변경 시 한 번만 통지하려면 skipRendering: true로 작업한 뒤 editor.render()를 호출합니다.
// 100개 단락에 클래스 추가 — change는 1회만 발생
editor.querySelectorAll('p', { skipRendering: true })
      .forEach(p => p.addClass('paragraph'));
editor.render();    // → change 이벤트 1회 발생
⑦ 실전 패턴 — 양식(Form) 입력 / 추출
에디터 안에 양식 표를 두고, 외부 폼과 데이터를 양방향 동기화하는 패턴입니다. → 라이브 데모 열기
// 입력: 폼 값을 에디터 표 셀에 채워넣기
function applyToEditor(form) {
  editor.getAPIModelById('cell-name').setText(form.name);
  editor.getAPIModelById('cell-phone').setText(form.phone);
  editor.getAPIModelById('cell-addr').setText(form.address);
}

// 추출: 에디터 표에서 값 읽어오기
function extractFromEditor() {
  return {
    name:    editor.getAPIModelById('cell-name').getText(),
    phone:   editor.getAPIModelById('cell-phone').getText(),
    address: editor.getAPIModelById('cell-addr').getText(),
  };
}
⑧ APIModel 메서드 요약
분류 메서드
식별·노드 getNode, isValid, getId, setId, getTagName, matches
속성 getAttribute, setAttribute, removeAttribute, hasAttribute
클래스 addClass, removeClass, hasClass, toggleClass
스타일 getStyle, setStyle, setCSS(prop, val), setCSS({...}), getCSS
내용 getText, setText, getHTML, setHTML, empty, remove
탐색 parent, closest, children, childNodes, nextSibling, prevSibling, find, findOne
삽입 append, prepend, insertBefore, insertAfter
측정 getRect, getWidth, getHeight
표 전용 insertRow, insertCol, deleteRow, deleteCol, setWidth, setHeight
9. 메뉴바 & UI 컨트롤
라온K·한컴 SDK 스타일의 상단 드롭다운 메뉴와 런타임 UI 제어 메서드. → 라이브 데모
① 메뉴바 활성화 (옵션)
// 기본 메뉴 활성화 (파일·편집·보기·삽입·서식·도구·도움말)
const editor = new WebEditor('#editor', {
  menubar: true,
});

// 커스텀 메뉴 구성
const editor2 = new WebEditor('#editor', {
  menubar: [
    { label: '게시글', items: [
      { label: '새 게시글', cmd: 'newDocument' },
      { label: '임시저장',  cmd: 'mySaveDraft' },
      { sep: true },
      { label: '발행',      cmd: 'myPublish' },
    ]},
    { label: '서식', items: [
      { label: '굵게',   cmd: 'bold',   shortcut: 'Ctrl+B' },
      { label: '기울임', cmd: 'italic', shortcut: 'Ctrl+I' },
    ]},
  ],
  onMenuCommand: (cmd, editor, item) => {
    if (cmd === 'mySaveDraft') {
      fetch('/api/draft', { method: 'POST', body: editor.getHTML() });
      return false; // 기본 처리 차단
    }
  },
});
② 기본 메뉴 명령 (내장)
분류 명령(cmd)
파일 newDocument, openHTML, save, saveAs, print, printPreview
편집 undo, redo, cut, copy, paste, selectAll, find
보기 toggleToolbar, toggleMenuBar, toggleStatusBar, toggleFullscreen, modeEdit, modeHTML, modePreview
삽입 insertImage, insertTable, createLink, insertVideo, insertEmoji, insertHorizontalRule
서식 bold, italic, underline, strikeThrough, clearFormat, clearCSSFormat
도구 toggleReadOnly, showWordCount, about, shortcuts
③ UI 컨트롤 메서드
// 개별 토글
editor.showMenuBar(true);    // 메뉴바 표시/숨김
editor.showToolbar(false);   // 툴바 표시/숨김
editor.showStatusBar(true);  // 상태바 표시/숨김
editor.setReadOnly(true);    // 읽기 전용 전환
editor.setFullscreen(true);  // 전체 화면 (F11, Esc 자동 지원)
editor.setHeight('500px');
editor.setWidth('100%');

// 런타임에 메뉴바 동적 추가/제거
editor.enableMenuBar();                // 기본 메뉴 추가
editor.enableMenuBar(customConfig);    // 커스텀 추가
editor.disableMenuBar();

// 일괄 적용
editor.setUI({
  menubar:    true,
  toolbar:    true,
  statusbar:  false,
  readOnly:   false,
  fullscreen: false,
  height:     '600px',
});

// 현재 상태 조회
const state = editor.getUI();
// → { toolbar:true, menubar:true, statusbar:true, readOnly:false, fullscreen:false, width:'100%', height:'400px' }
④ 키보드 단축키
  • F11 — 전체 화면 전환
  • Esc — 전체 화면 종료
  • 메뉴 호버 — 다른 메뉴로 자동 전환
  • 메뉴 외부 클릭 / Esc — 드롭다운 닫기
10. MS Word 가져오기
.docx 파일을 에디터로 가져오는 별도 플러그인입니다. 외부 라이브러리 0 — 브라우저 내장 DecompressionStream + DOMParser만 사용. Standard 에디션에 기본 포함.
사용 방법
<!-- 1. 코어 + 플러그인 함께 로드 (순서 중요) -->
<script src="dist/webeditor.js"></script>
<script src="dist/plugins/wordimport-native.js"></script>

<script>
const editor = new WebEditor('#editor', {
  menubar: true,
  plugins: ['format', 'image', 'table', 'wordimport'],  // ← 추가
});

// 프로그래매틱 호출
editor.importWordFile(file, {
  mode: 'cursor',    // 'replace' | 'cursor' | 'append'
  silent: false,    // true면 다이얼로그 생략
});
</script>
진입 경로
  • ① 메뉴바 → 파일 → MS Word 가져오기…
  • ② 툴바의 워드 아이콘 (W)
  • ③ 에디터에 .docx 파일 드래그앤드롭
  • editor.importWordFile(file, options) API 호출
지원·미지원
지원 미지원
단락·정렬·들여쓰기
제목 스타일 (H1~H6)
굵게/기울임/밑줄/취소선
색상·폰트·크기·배경
표 + 셀 병합 (colspan/rowspan)
글머리·번호 목록
하이퍼링크
이미지 (PNG/JPG, base64)
페이지 나누기
SmartArt · 도형
수식 (MathML)
머리글 / 꼬리글
각주 / 미주
VBA 매크로
변경 내용 추적
※ 미지원 요소는 변환 후 경고 모달에 목록으로 표시됩니다. 더 높은 변환 품질이 필요하면 Premium 에디션 (mammoth.js 엔진) 사용을 고려하세요.
브라우저 지원

Chrome 80+ · Edge 80+ · Firefox 113+ · Safari 16.4+ (이전 버전 미지원)

11. TypeScript 사용
dist/webeditor.d.ts가 포함되어 있어 타입 추론이 자동으로 동작합니다. 전체 인터페이스 정의(WebEditorOptions·LicenseInfo·메서드 시그니처)는 📑 API 레퍼런스 §11 TypeScript 타입을 참조하세요.
import WebEditor, { WebEditorOptions } from '@webeditor/core';

const options: WebEditorOptions = {
  height:    '500px',
  onChange:  (html: string) => saveContent(html),
};

const editor = new WebEditor('#editor', options);

// 타입 안전한 API 사용
const html: string = editor.getHTML();
editor.setHTML('<p>안녕하세요!</p>');
editor.on('change', (html: string) => console.log(html));
12. v2.4.0 신규 기능
v2.4.0에서 추가된 옵션·메서드·이벤트 모음입니다. 자세한 타입은 dist/webeditor.d.ts 참조.
자동 저장 (AutoSave)
localStorage 주기 저장 + 이탈 경고. 복구 API 제공.
const editor = new WebEditor('#editor', {
  autoSave: {
    interval: 30000,        // 저장 주기(ms)
    storageKey: 'my-doc',   // 기본: we-autosave-{id}
    unloadWarning: true,    // 변경 후 이탈 시 경고
    onSave: (html) => {}    // false 반환 시 저장 억제
  }
});

// 페이지 로드 시 복구
const saved = editor.getAutoSaved();        // string | null
if (saved) { editor.setHTML(saved); editor.clearAutoSaved(); }
editor.saveNow();                            // 즉시 저장
PDF 저장 / 확대·축소
editor.saveToPDF({ title: '문서', hideToolbar: true });  // 본문만 인쇄(PDF)
editor.saveToPDFInWindow({ title: '문서' });            // 새 창 출력 후 인쇄

editor.setZoom(1.5);   // 0.25 ~ 4.0 (범위 밖 RangeError)
editor.getZoom();      // 현재 비율
editor.on('zoom', ({ ratio }) => console.log(ratio));
부유 미니툴바 / 새 링크 기본 target
new WebEditor('#editor', {
  inlineToolbar: true,                  // 텍스트 선택 시 부유 서식 툴바
  hyperLinkDefaultTarget: '_blank'      // 새 링크 기본 새 창
});
안전한 HTML 출력 (getSafeHTML)
editor.getSafeHTML();                          // script/on*/javascript: 제거
editor.getSafeHTML({
  allowedTags: ['p','b','i','a'],            // 외 태그는 언랩(텍스트 보존)
  allowedAttrs: ['href'],
  imageRewriter: (src) => 'https://cdn/' + src
});
금칙어 / 개인정보 검출
const editor = new WebEditor('#editor', {
  privacy:   { detect: ['ssn','phone','email','credit-card','bank-account'] },
  profanity: { words: ['금칙어1','금칙어2'], maskChar: '*' }  // words: 배열 또는 URL
});

const r = await editor.validate();   // { profanity:[...], privacy:[...] }
await editor.maskSensitiveData();      // 감지 항목 마스킹(반환: 개수)
업로드 보안 (CSRF / 헤더)
new WebEditor('#editor', {
  uploadHeaders: { 'X-App': 'dee' },        // 커스텀 헤더
  csrfCookie: 'XSRF-TOKEN',               // 쿠키값 → X-CSRF-Token 자동 주입
  uploadHandler: (file, headers) => fetch('/upload', { method:'POST', headers, body: makeForm(file) })
                                  .then(r => r.json()).then(j => j.url)
});
신규 이벤트 / 상태 메서드
editor.on('imageInserted', ({ src, file, node }) => {});  // 이미지 DOM 부착 시
editor.on('imageUploaded', ({ src, file, node }) => {});  // 업로드 URL 확정 시
editor.on('error', ({ context, error }) => {});            // 내부 오류 수집

editor.isEmpty();   // 본문 비었는지 (공백·빈 단락은 빈 것으로, 이미지·표는 false)