aeriz WYSIWYG
언어

aeriz 에디터

한국어 친화 contentEditable WYSIWYG 에디터 — 의존성 0, 모듈식 블록, 서버 라운드트립을 위한 typed BlockSnapshot.
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. 서버 저장/복원에 안전.
  • 다국어 UIEN / KO / JA / ZH 4 개 로케일 — toolbar / 다이얼로그 / 팝오버 / 상태 바 모든 정적 문자열 사전화.
  • 의존성 0vanilla 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 을 파싱 하지 않으므로 에디터와 같은 시각이 보장됩니다.

render
viewer.getBlocks()
— 블록이 없습니다 —

EditorState + getHTML()

현재 활성 블록 / 블록 배열 / portable HTML 출력 / 미리보기의 실시간 디버그 뷰입니다.

active
active: null
inline: —
blocks []
— 블록이 없습니다 —
getHTML()
<p><br></p>
미리보기

블록 유형

    서식 유형

    툴바에서 사용할 수 있는 서식 기능 목록 — 어떤 블록에서 활성화되며 어떤 단축키와 짝지어지는지 정리.

    기능 허용 블록 단축키 비고
    fontFamily인라인 6 종9 종 (시스템 / Pretendard / Noto / 나눔 등) lazy CDN 로드
    fontSizeparagraph / lists / blockquote / codeBlock / table12 단계 (12 ~ 48px) — Range API span wrap
    bold / italic / underline / strike블록별 화이트리스트 (heading=italic만 / blockquote=italic제외)Ctrl+B / Ctrl+I / Ctrl+U / Ctrl+Shift+XstyleWithCSS=true → <span style> 으로 적용
    color / backgroundColorparagraph / lists / tableexecCommand foreColor / hiliteColor + LRU 8 개
    alignparagraph / heading / blockquote / tablestyle.textAlign 직접 — blockquote DIV 변환 버그 회피
    cellBgtable다중 셀 마킹 일괄 / !important 로 <th> stylesheet 우선
    tableMenutable행/열 추가·삭제 — 마지막 1 행/열 보존
    codeLangcodeBlock16 종 — 자체 토크나이저, blur/input 시 자동 highlight

    API 메소드

    AerizEditor / AerizViewer 의 공개 메소드. 모두 인스턴스 메소드이며, destroy() 호출 후엔 사용할 수 없습니다.

    메소드 인자 반환 비고
    new AerizEditor(options)EditorOptionsAerizEditorelement / theme / locale / initialHTML / onChange / onImageUpload 등
    editor.getHTML()stringportable HTML — applyBlockInlineStyles 베이크된 자급자족 HTML
    editor.getRawHTML()string에디터 원본 innerHTML — 디버그/검증용
    editor.setHTML(html)stringvoid본문 교체 후 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)stringBlock | nulldata-block-id 로 블록 조회
    editor.focus()voidcontentEditable 영역에 포커스
    editor.setTheme(theme)'light' | 'dark'voiddata-theme 변경 — CSS 변수 토큰 전환
    editor.onStateChange(listener)(state) => void() => void반환된 함수 호출로 unsubscribe
    editor.destroy()voidDOM / observer / listener 정리 — 멀티 호출 안전
    new AerizViewer(options)ViewerOptionsAerizViewer읽기 전용 — 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();
      }
    }