임베드

스크립트 한 줄로 내 사이트나 앱에 에디터를 붙일 수 있습니다. Plus 라이선스 키가 있으면 워터마크가 사라집니다.

설치

아래 스니펫을 페이지에 붙이면 끝입니다. 별도 의존성 없이 82KB 한 파일로 동작합니다.

<div id="typdit-editor"></div>
<script src="https://typdit.com/embed/v1.js"></script>
<script>
  const editor = Typdit.create(document.getElementById('typdit-editor'), {
    placeholder: 'Write here',
    licenseKey: 'YOUR_KEY',
    onChange: (snapshot) => console.log(snapshot),
  });
</script>

에디터 지면에서 바로 가져가기

설치 코드를 손으로 쓰지 않아도 됩니다. 에디터 지면에서 모드와 테마를 원하는 대로 맞춰 놓고 아래로 내리면, 그 설정이 그대로 적힌 설치 코드가 있습니다. 기본값과 같은 옵션은 적히지 않으므로 무엇이 특별한 설정인지 한눈에 보입니다.

문서까지 복사를 누르면 지금 화면에 쓴 내용이 snapshot 옵션으로 함께 들어갑니다. 템플릿을 열어 뼈대를 확인한 다음 그 뼈대째로 가져가고 싶을 때 쓰면 됩니다.

옵션 전체

Typdit.create(element, options) 의 두 번째 인자로 넘깁니다. 전부 생략 가능하고, 표기한 값이 기본값입니다.

옵션설명
snapshot초기 문서(JSON 스냅샷, 아래 포맷 절 참조). 없으면 빈 문서로 시작
theme: 'light' | 'dark'에디터 테마(기본 light)
mode: 'notion'노션 스타일 프리셋: 툴바를 숨기고 블록 핸들을 켭니다(아래 절 참조). 기본 classic
handles: true블록 핸들 개별 토글(mode 프리셋보다 우선)
writingMode: 'paper'글쓰기 모드. plain, focus, book, paper, essay, script, techdoc 중 하나이거나 프리셋 객체. 조판과 계량, 목차, 각주, 대본 흐름이 함께 바뀝니다
writingMode: { id, extends }내장 모드 위에 몇 칸만 덮어써 새 모드를 만듭니다. extends 를 빼면 아무것도 켜지지 않은 바닥에서 시작합니다
writingLabels모드 UI 문구 교체(계량 이름, 목차, 각주, 목표). 부분 지정 가능하고 지정하지 않은 항목은 영어 기본값
writingSlashLabels모드 전용 슬래시 항목 문구(각주, 대본 블록)
handleLabels블록 핸들 문구 교체(부분 지정 가능)
toolbar: false상단 툴바 숨김(기본 표시)
menus: false슬래시 커맨드·선택 버블 메뉴 끄기(기본 켬)
markdown: false# 제목, - 리스트 같은 마크다운 입력 단축 끄기(기본 켬)
typography: false굽은따옴표·말줄임표 자동 치환 끄기(기본 켬)
placeholder빈 문서에 표시할 힌트 문구
autofocus: true생성 직후 에디터에 포커스(기본 꺼짐)
toolbarLabels툴바 문구 교체(부분 지정 가능, 아래 절 참조)
licenseKeyPlus 라이선스 키. 도메인 검증 후 워터마크 제거
onChange(snapshot)내용이 바뀔 때마다 최신 스냅샷을 전달
onRequestImage()이미지 업로더 훅. {src, alt?} 를 resolve 하면 삽입, null 은 취소. 없으면 툴바가 URL 입력으로 폴백
onRequestFile()파일 첨부 훅. {src, name?, size?} 반환. 이 훅을 줄 때만 파일 버튼이 렌더됨
onRequestVideo()영상 파일 업로드 훅. {src} 반환. 지정하면 영상 입력 행에 업로드 버튼이 추가됨(링크 입력은 항상 가능)

노션 스타일 모드

mode: 'notion' 은 툴바를 숨기고 블록 핸들을 켭니다. 블록에 마우스를 올리면 왼쪽에 추가(+)와 메뉴(⋮⋮) 버튼이 나타나고, 메뉴에서 위, 아래 이동, 복제, 삭제, 블록 전환을 할 수 있습니다. 서식은 슬래시 커맨드와 선택 버블 메뉴를 그대로 사용합니다.

툴바가 없으면 이미지, 영상, 파일 삽입 항목은 슬래시 메뉴에서도 빠집니다(삽입 UI가 툴바 소유라서). 핸들과 첨부가 둘 다 필요하면 toolbar: true 를 함께 넘기면 됩니다. 핸들과 툴바는 공존합니다.

Typdit.create(el, {
  mode: 'notion',
  handleLabels: { moveUp: '위로 이동', moveDown: '아래로 이동', delete: '삭제' },
});

인스턴스 메서드

Typdit.create() 가 돌려주는 핸들로 문서를 읽고 씁니다. 저장은 전적으로 호스트 사이트의 몫입니다.

메서드설명
getSnapshot()현재 문서를 JSON 스냅샷으로 반환
setSnapshot(json)스냅샷으로 문서 전체를 교체(불러오기)
getText()서식 없는 순수 텍스트 반환(검색 색인·글자 수 세기용)
focus()에디터에 포커스 이동
destroy()에디터 제거·리스너 해제. SPA 에서 화면을 떠날 때 호출
versionSDK 버전 문자열(현재 1.0.0)

스냅샷 포맷

스냅샷은 그대로 저장하고 그대로 되돌려주면 되는 평면 JSON 입니다. text 의 줄 하나가 blocks 의 항목 하나와 1:1 로 대응하고, marks 는 text 기준 오프셋 구간입니다. 모르는 블록, 마크 타입은 불러올 때 조용히 건너뛰므로, 새 버전이 만든 스냅샷을 구버전이 열어도 깨지지 않습니다.

{
  "version": 1,
  "text": "제목\n첫 문단입니다.",
  "blocks": [
    { "type": "heading", "attrs": { "level": 1 } },
    { "type": "paragraph" }
  ],
  "marks": [
    { "type": "bold", "from": 3, "to": 7 }
  ]
}

자동 저장 붙이기

onChange 는 타건마다 불립니다. 서버 저장은 디바운스를 걸어 손을 뗀 뒤 한 번만 보내는 것이 일반적입니다.

let timer;
const editor = Typdit.create(el, {
  onChange: (snapshot) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fetch('/api/save', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(snapshot),
      });
    }, 800);
  },
});

이미지·파일 업로드 연동

에디터는 파일을 직접 업로드하지 않습니다. 훅이 파일 선택과 업로드를 맡고 URL 만 돌려주면, 삽입과 렌더는 에디터가 합니다. 훅 없이도 이미지는 URL 입력으로 동작하고, 파일 버튼은 onRequestFile 을 줄 때만 나타납니다.

Typdit.create(el, {
  onRequestImage: async () => {
    const file = await pickFile('image/*');   // 파일 선택 UI는 호스트 구현
    if (!file) return null;                   // null = 취소
    const src = await uploadToMyServer(file); // 업로드도 호스트 구현
    return { src, alt: file.name };
  },
  onRequestFile: async () => {
    const file = await pickFile('*/*');
    if (!file) return null;
    const src = await uploadToMyServer(file);
    return { src, name: file.name, size: file.size };
  },
  onRequestVideo: async () => {
    const file = await pickFile('video/*');
    return file ? { src: await uploadToMyServer(file) } : null;
  },
});

툴바 문구 바꾸기

기본 문구는 영어입니다. toolbarLabels 에 바꿀 키만 부분적으로 넘기면 나머지는 기본값이 유지됩니다. 키 전체: text, quote, code, divider, table, image, video, file, upload, font, fontDefault, color, highlight, none, alignLeft, alignCenter, alignRight, imageUrl, videoUrl, link, linkApply, linkRemove, undo, redo.

Typdit.create(el, {
  toolbarLabels: {
    text: '본문', quote: '인용', code: '코드', divider: '구분선',
    image: '이미지', video: '영상', link: '링크',
    undo: '되돌리기', redo: '다시 실행',
  },
});

React 에서 쓰기

ref 로 컨테이너를 잡고 마운트 시 생성, 언마운트 시 destroy() 를 호출합니다.

import { useEffect, useRef } from 'react';

function TypditEditor({ onChange }) {
  const ref = useRef(null);
  useEffect(() => {
    const editor = window.Typdit.create(ref.current, { onChange });
    return () => editor.destroy();
  }, []);
  return <div ref={ref} />;
}

Vue 에서 쓰기

onMounted 에서 생성하고 onBeforeUnmount 에서 destroy() 를 호출합니다.

<template><div ref="host"></div></template>

<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
const host = ref(null);
let editor;
onMounted(() => { editor = window.Typdit.create(host.value, {}); });
onBeforeUnmount(() => editor?.destroy());
</script>

SSR 프레임워크(Next.js 등)에서 쓰기

스크립트는 브라우저에서만 실행됩니다. 서버 렌더 중에는 window 가 없으므로 클라이언트 전용 컴포넌트에서 스크립트를 로드한 뒤 생성하세요.

'use client';
import { useEffect, useRef } from 'react';

export default function Editor() {
  const ref = useRef(null);
  useEffect(() => {
    let editor;
    const s = document.createElement('script');
    s.src = 'https://typdit.com/embed/v1.js';
    s.onload = () => { editor = window.Typdit.create(ref.current, {}); };
    document.head.appendChild(s);
    return () => editor?.destroy();
  }, []);
  return <div ref={ref} />;
}

한 페이지에 여러 에디터

Typdit.create() 호출마다 독립 인스턴스가 만들어지므로 한 페이지에 몇 개든 둘 수 있습니다. 스타일은 첫 생성 때 style 태그 하나로만 주입되고, 각 인스턴스는 자기 핸들의 destroy() 로 개별 제거합니다.

라이선스와 워터마크

  • 키 없이도 에디터는 전부 동작하며, 하단에 작은 워터마크가 붙습니다.
  • Plus 구독자는 에디터 지면의 임베드 카드에서 도메인별 키를 발급합니다. 키는 등록한 도메인과 그 서브도메인에서 유효합니다(*.example.com 표기도 같은 의미).
  • 키의 만료는 발급 시점의 구독 만료와 같습니다. 구독을 연장했다면 키를 다시 발급하세요.
  • 검증에 실패하거나 네트워크가 막혀 있어도 에디터는 차단되지 않습니다. 워터마크가 남을 뿐, 에러도 나지 않습니다.

네트워크와 CSP

  • 문서 내용은 어디로도 전송되지 않습니다. 에디터는 콘텐츠에 대해 네트워크 요청을 하지 않고, 저장은 전적으로 호스트 사이트가 합니다.
  • 유일한 요청은 licenseKey 를 지정했을 때의 검증 1회입니다(firestore.googleapis.com 읽기 전용).
  • CSP 를 쓰는 사이트는 script-src 에 typdit.com 을, 키를 쓴다면 connect-src 에 firestore.googleapis.com 을 허용합니다. 본문에 넣는 이미지·영상은 별개로, 그 출처를 img-src·frame-src 에서 열어야 보입니다(유튜브 임베드는 youtube-nocookie.com).
  • 스타일은 td- 접두사 클래스로 스코프된 style 태그 하나로 주입되어 호스트 CSS 와 섞이지 않습니다.

버전 정책

/embed/v1.js 는 메이저 버전이 고정된 URL 입니다. v1 URL 안에서는 하위 호환이 유지되고, 호환이 깨지는 변경은 새 URL(v2)로 나갑니다. 현재 로드된 버전은 런타임에 Typdit.version 으로 확인할 수 있습니다.

다음: AI 도구 →