aeriz 에디터
new AerizEditor() /
new AerizViewer() ·
의존성 0개 · v1.0.0
- 블록 기반 구조 — 14 종 BlockType (단락/제목/목록/인용/코드/표/이미지/링크 카드/유튜브/파일/HTML 가져오기 등) 를 동일한 레지스트리로 처리. 직렬화/렌더 라운드트립이 동일한 트리.
- 풍부한 인라인 서식 — 굵게/기울임/밑줄/취소선, 글꼴 9 종 + 글자 크기 12 단계, 글자색/배경색, 정렬, 그리고 블록 종류별 화이트리스트로 의미를 보존.
- 코드 블록 + syntax highlight — 자체 토크나이저로 16 개 언어(JS/TS/Python/Java/Kotlin/Go/Rust/C/C++/C#/HTML/CSS/SQL/Bash/JSON/Markdown). 라이브 편집 + 라이트/다크 색 자동.
- 표 편집 — 행/열 추가·삭제, 다중 셀 드래그-셀렉션, 셀 배경색, 가장자리 드래그로 열 너비/행 높이 조정.
- 외부 가져오기 — HTML(iframe + computed style 베이크), Markdown(자체 파서), 파일 첨부 카드, og:image 링크 미리보기 카드, YouTube 임베드.
- 타입 안전 직렬화 — BlockSnapshot 이 discriminated union — kind 로 narrow 하면 attrs 가 자동 typed. 서버 저장/복원에 안전.
- 다국어 UI — EN / KO / JA / ZH 4 개 로케일 — toolbar / 다이얼로그 / 팝오버 / 상태 바 모든 정적 문자열 사전화.
- 의존성 0 — vanilla TypeScript + CSS. React / Vue / Svelte / Angular / jQuery 어디든 mount 가능.
Quick Start
아래 HTML 한 페이지를 그대로 복사해 브라우저로 열면 바로 동작하는 에디터가 뜹니다. 빌드 도구 / npm install 없이 jsDelivr CDN 만으로 가능.
<!-- 1. CSS / 2. IIFE 스크립트 / 3. mount + new AerizEditor() --> <!doctype html> <html> <head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aeriz/wysiwyg@1.0.0/dist/aeriz-wysiwyg.min.css"> <script src="https://cdn.jsdelivr.net/npm/@aeriz/wysiwyg@1.0.0/dist/aeriz-wysiwyg.min.js"></script> </head> <body> <div id="editor"></div> <script> new AerizEditor({ element: document.getElementById('editor'), theme: 'dark', locale: 'ko', }); </script> </body> </html>
CDN 설치
jsDelivr 의 npm mirror 를 통해 빌드 결과물을 직접 가져올 수 있습니다. 빌드 도구 없이 HTML 한 페이지에 즉시 임베드할 수 있어 데모 / 프로토타입 / 정적 사이트 / 사내 wiki 에서 가장 빠른 길.
/* CSS — 스타일시트 */ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aeriz/wysiwyg@1.0.0/dist/aeriz-wysiwyg.min.css">
/* IIFE — globalThis.AerizEditor / globalThis.AerizViewer */ <script src="https://cdn.jsdelivr.net/npm/@aeriz/wysiwyg@1.0.0/dist/aeriz-wysiwyg.min.js"></script> <script> const editor = new AerizEditor({ element: document.getElementById('editor'), theme: 'dark', locale: 'ko', }); </script>
/* ESM — 모던 브라우저의 native ES module */ <script type="module"> import { AerizEditor } from 'https://cdn.jsdelivr.net/npm/@aeriz/wysiwyg@1.0.0/dist/aeriz-wysiwyg.esm.min.js'; const editor = new AerizEditor({ element: document.getElementById('editor'), theme: 'dark', locale: 'ko', }); </script>
버전 고정을 위해 `@1.0.0` 처럼 명시 권장. `@latest` 또는 `@^1.0.0` 도 가능하지만 캐시 무효화 시점이 달라질 수 있습니다.
에디터
본문 입력과 툴바 동작을 확인할 수 있는 플레이그라운드입니다.
AerizViewer (읽기 전용)
viewer.setBlocks(editor.serialize()) 호출로 상태 모델(BlockSnapshot 배열) 을 받아 동일한 BlockType 레지스트리로 렌더합니다. HTML 을 파싱 하지 않으므로 에디터와 같은 시각이 보장됩니다.
— 블록이 없습니다 —
EditorState + getHTML()
현재 활성 블록 / 블록 배열 / portable HTML 출력 / 미리보기의 실시간 디버그 뷰입니다.
active: null inline: —
— 블록이 없습니다 —
<p><br></p>
블록 유형
서식 유형
툴바에서 사용할 수 있는 서식 기능 목록 — 어떤 블록에서 활성화되며 어떤 단축키와 짝지어지는지 정리.
| 기능 | 허용 블록 | 단축키 | 비고 |
|---|---|---|---|
fontFamily | 인라인 6 종 | — | 9 종 (시스템 / Pretendard / Noto / 나눔 등) lazy CDN 로드 |
fontSize | paragraph / lists / blockquote / codeBlock / table | — | 12 단계 (12 ~ 48px) — Range API span wrap |
bold / italic / underline / strike | 블록별 화이트리스트 (heading=italic만 / blockquote=italic제외) | Ctrl+B / Ctrl+I / Ctrl+U / Ctrl+Shift+X | styleWithCSS=true → <span style> 으로 적용 |
color / backgroundColor | paragraph / lists / table | — | execCommand foreColor / hiliteColor + LRU 8 개 |
align | paragraph / heading / blockquote / table | — | style.textAlign 직접 — blockquote DIV 변환 버그 회피 |
cellBg | table | — | 다중 셀 마킹 일괄 / !important 로 <th> stylesheet 우선 |
tableMenu | table | — | 행/열 추가·삭제 — 마지막 1 행/열 보존 |
codeLang | codeBlock | — | 16 종 — 자체 토크나이저, blur/input 시 자동 highlight |
API 메소드
AerizEditor / AerizViewer 의 공개 메소드. 모두 인스턴스 메소드이며, destroy() 호출 후엔 사용할 수 없습니다.
| 메소드 | 인자 | 반환 | 비고 |
|---|---|---|---|
new AerizEditor(options) | EditorOptions | AerizEditor | element / theme / locale / initialHTML / onChange / onImageUpload 등 |
editor.getHTML() | — | string | portable HTML — applyBlockInlineStyles 베이크된 자급자족 HTML |
editor.getRawHTML() | — | string | 에디터 원본 innerHTML — 디버그/검증용 |
editor.setHTML(html) | string | void | 본문 교체 후 normalizeContent + assignMissingIds |
editor.serialize() | — | BlockSnapshot[] | 서버 라운드트립용 — discriminated union |
editor.getBlocks() | — | Block[] | live element 참조 포함 (Block.element) |
editor.getActiveBlock() | — | Block | null | 현재 selection 의 블록 — 다중 블록 선택 시 null |
editor.setBlockType(kind, attrs?, targetId?) | BlockKind, BlockAttrs?, string? | void | 활성 블록 또는 targetId 의 블록을 새 종류로 변환 |
editor.findBlockById(id) | string | Block | null | data-block-id 로 블록 조회 |
editor.focus() | — | void | contentEditable 영역에 포커스 |
editor.setTheme(theme) | 'light' | 'dark' | void | data-theme 변경 — CSS 변수 토큰 전환 |
editor.onStateChange(listener) | (state) => void | () => void | 반환된 함수 호출로 unsubscribe |
editor.destroy() | — | void | DOM / observer / listener 정리 — 멀티 호출 안전 |
new AerizViewer(options) | ViewerOptions | AerizViewer | 읽기 전용 — element / initialBlocks / theme |
viewer.setBlocks(snapshots) | BlockSnapshot[] | void | 상태 모델 받아 같은 BlockType 레지스트리로 렌더 |
viewer.getHTML() | — | string | 에디터와 같은 portable HTML 출력 |
프레임워크별 초기화
라이프사이클(mount / unmount) 에 맞춰 에디터를 만들고 destroy 까지 깔끔히 정리하는 방법. 모든 예시는 동일한 옵션 (locale / theme / initialHTML / onChange) 을 받습니다.
import { AerizEditor } from '@aeriz/wysiwyg'; import '@aeriz/wysiwyg/style.css'; const editor = new AerizEditor({ element: document.getElementById('editor'), theme: 'dark', locale: 'ko', // 'en' | 'ko' | 'ja' | 'zh' initialHTML: '<p>Hello</p>', onChange: (html) => console.log(html), }); // 페이지 unload 시 정리 window.addEventListener('beforeunload', () => editor.destroy());
import $ from 'jquery'; import { AerizEditor } from '@aeriz/wysiwyg'; import '@aeriz/wysiwyg/style.css'; $(function () { const editor = new AerizEditor({ element: $('#editor')[0], theme: 'dark', locale: 'ko', }); $(window).on('beforeunload', () => editor.destroy()); });
<!-- Editor.vue --> <template> <div ref="el"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; import { AerizEditor } from '@aeriz/wysiwyg'; import '@aeriz/wysiwyg/style.css'; const el = ref(null); let editor; onMounted(() => { editor = new AerizEditor({ element: el.value, theme: 'dark', locale: 'ko', }); }); onBeforeUnmount(() => editor?.destroy()); </script>
import { useEffect, useRef } from 'react'; import { AerizEditor } from '@aeriz/wysiwyg'; import '@aeriz/wysiwyg/style.css'; export function Editor() { const ref = useRef(null); useEffect(() => { const editor = new AerizEditor({ element: ref.current, theme: 'dark', locale: 'ko', }); return () => editor.destroy(); }, []); return <div ref={ref} />; }
<script> import { onMount, onDestroy } from 'svelte'; import { AerizEditor } from '@aeriz/wysiwyg'; import '@aeriz/wysiwyg/style.css'; let el; let editor; onMount(() => { editor = new AerizEditor({ element: el, theme: 'dark', locale: 'ko', }); }); onDestroy(() => editor?.destroy()); </script> <div bind:this={el}></div>
import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy, } from '@angular/core'; import { AerizEditor } from '@aeriz/wysiwyg'; @Component({ selector: 'app-editor', template: '<div #editorEl></div>', }) export class EditorComponent implements AfterViewInit, OnDestroy { @ViewChild('editorEl') editorEl!: ElementRef; private editor?: AerizEditor; ngAfterViewInit() { this.editor = new AerizEditor({ element: this.editorEl.nativeElement, theme: 'dark', locale: 'ko', }); } ngOnDestroy() { this.editor?.destroy(); } }