ProseMirror Guide (1)

정현석·2020년 12월 5일
3

ProseMirror

목록 보기
1/7

https://prosemirror.net/docs/guide/

ProseMirror Guide

본 문서는 라이브러리의 다양한 개념에 대한 설명을 합니다. 최소한 View Component Section까지는 봐야 전체적인 이해가 갈겁니다.

Introduction

프로즈미러는 커스터마이징이 편한 Wysiwyg 에디터 툴입니다.

프로즈미러의 주원칙은 여러분이 document에 일어나는 모든 것에 완전한 컨트롤을 가지는 것입니다. 이 document는 HTML 더미가 아니고, 여러분이 정의할 수 있는 구조에 알맞은 데이터만을 가집니다. 모든 업데이트는 싱글 포인트를 통하고, 그곳에서 이를 관찰하고 다룰 수 있습니다.

코어 라이브러리는 모든걸 포함하지 않습니다. 우리는 모듈성과 커스터마이징 가능성을 우선시하기 때문이죠. 그렇기 때문에 이는 완제품 장난감보다는 레고에 가깝습니다.

어떤 편집을 하든 네가지 필수 모듈이 필요하지만, 그것들을 교체하는것도 가능합니다.

필수 모듈들은 다음과 같습니다:

prosemirror-model

에디터의 document model을 정의합니다. 이는 에디터의 content를 묘사하는 자료구조입니다.

prosemirror-state

에디터의 전체 state를 묘사하는 자료구조입니다. selection이나, 한 상태에서 다음 상태로 넘어가는 transaction 시스템을 포함합니다.

prosemirror-view

유저의 인터페이스 컴포넌트를 구현합니다. 유저가 이를 편집가능하게 하는 요소들이며, 유저와의 상호작용을 핸들링합니다.

prosemirror-transform

기록되고 재생될 수 있는 형태로 문서를 수정하는 기능을 포함합니다. 이것은 state 모듈의 transaction의 기반이 됩니다. 그리고 undo history 나 collaborative editing을 가능하게 합니다.

이에 더해, basic editing commands, binding keys, undo history, input macros, collaborative editing, 간단한 document 스키마, 그리고 더 많은 것이 GitHub 프로즈미러 재단에 포함됩니다.

ProseMirror가 브라우저에서 불러쓸 수 있는 하나의 스크립트로 배포되지는 않기 때문에 여러분은 이를 이용하기 위해 번들러를 이용하고 싶어할 것입니다. 예제로 이에 대한것을 제공합니다.

My first editor

최소한의 기능을 하는 에디터를 만들기 위해서 레고 조각은 다음과같이 맞춰집니다.

import {schema} from "prosemirror-schema-basic"
import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"

let state = EditorState.create({schema})
let view = new EditorView(document.body, {state})

프로즈미러는 여러분이 스키마를 정의해야하고, 그 스키마가 document의 동작을 정의합니다. 그렇기 때문에 처음 해야할 것은 기본 스키마 모듈을 임포트 하는 것입니다.

이 스키마는 다음으로 state를 만드는데 사용되고, state는 빈 document를 생성할 것입니다. 그리고 document의 시작부분에 기본 selection을 가지겠지요. 마지막으로 view가 state에 대해 생성되고, document.body에 붙습니다. 이는 state의 document를 편집 가능한 DOM node 로써 렌더링하고, 사용자가 여기에 타이핑을 할때마다 state transaction을 생성합니다.

에디터는 아직 쓸만하지 않습니다. 예를 들어 만약 enter를 누른다면, 아무것도 일어나지 않습니다. 코어 라이브러리가 enter에 대해 무엇을 해야할지 전혀 정한것이 없기 때문입니다. 곧 그것을 처리해볼겁니다.

Transactions

사용자가 타이핑을 할때, 혹은 view와 상호작용 할 때, 이는 'state transactions'를 만들어냅니다. 이는 document를 즉석에서 수정하는 것이 아니라, 암시적으로 이에 대한 state를 업데이트 하는 것을 의미합니다. 모든 변화는 transaction을 만들어냅니다. 그리고 이 transaction은 state에 행해져야하는 변화를 묘사합니다. 이것이 적용되면 새로운 state를 만들어내고, 이것이 view를 업데이트하는데 사용됩니다.

기본적으로 이것은 보이지 않는 곳에서 일어나는데, 여러분이 직접 플러그인을 작성하거나 view를 설정할 때 hook할 수 있습니다. 예를 들어서 다음 코드는 transaction이 발생될 때 마다 dispatchTransaction prop을 추가합니다.

// (Imports omitted)

let state = EditorState.create({schema})
let view = new EditorView(document.body, {
  state,
  dispatchTransaction(transaction) {
    console.log("Document size went from", transaction.before.content.size,
                "to", transaction.doc.content.size)
    let newState = view.state.apply(transaction)
    view.updateState(newState)
  }
})

모든 state 업데이트는 upateState를 거쳐야하고, 모든 일반적인 editing update는 transaction을 dispatching할 것입니다.

Plugins

플러그인은 에디터의 행동을 확장하는데 사용됩니다. keymap 플러그인 같은것은 매우 간단하고, 행동을 키보드 입력과 연결하는데 사용됩니다. 다른건 조금 더 복잡한데, history plugin은 transaction을 관찰하여 저장함으로써 사용자가 원할 때 되돌릴 수 있게 합니다.

이 두가지 플러그인을 우리 에디터에 넣어보죠.

// (Omitted repeated imports)
import {undo, redo, history} from "prosemirror-history"
import {keymap} from "prosemirror-keymap"

let state = EditorState.create({
  schema,
  plugins: [
    history(),
    keymap({"Mod-z": undo, "Mod-y": redo})
  ]
})

let view = new EditorView(document.body, {state})

플러그인들은 state를 생성할 때 등록됩니다(이는 state transaction에 접근하기 위해서입니다). 이 history가 활성화된 state를 위한 view를 생성하고 나서는 Ctrl-Z를 통해 마지막 변화를 되돌릴 수 있습니다.

Commands

앞선 예제에서 단축키에 연결한 undo와 redo 값은 'command'라고 불리는 특별한 함수입니다. 대부분의 편집 행동은 단축키에 연결하거나, 메뉴에 hooked up되거나, 사용자에게 노출 가능한 command로 씌여집니다.

prosemirror-commands 패키지는 enter와 delete와 같은 몇몇 기본 편집 단축키와 편집 command를 제공합니다.

// (Omitted repeated imports)
import {baseKeymap} from "prosemirror-commands"

let state = EditorState.create({
  schema,
  plugins: [
    history(),
    keymap({"Mod-z": undo, "Mod-y": redo}),
    keymap(baseKeymap)
  ]
})
let view = new EditorView(document.body, {state})

이쯤에서, 여러분은 기본적으로 작동하는 에디터를 갖게 됩니다.

메뉴를 추가하기 위해선, schema별로 키바인딩을 추가로 해주는 등 prosemirror-example-setup 패키지를 한번 살펴보시기 바랍니다. 이는 기본 에디터를 셋업하기 위한 여러 플러그인들을 제공합니다. 그러나 이름이 의미하는것과 같이 production-level 라이브러리라기 보단 예제에 가깝습니다. 실제 deployment를 위해선, 여러분이 원하는 것에 알맞는 커스텀 코드로 교체해야합니다.

Content

state의 document는 state의 doc 프로퍼티에 있습니다. 이는 읽기전용 자료구조로써, document를 노드의 hierarchy로 표현합니다. broweser DOM과 사뭇 비슷하죠. 단순한 document의 예제로는 두개의 paragraph 노드를 포함하는 doc 노드를 생각할 수 있습니다. 각각의 paragraph 노드는 또 각각 하나의 text노드를 포함하는 식으로요. 가이드에서 더 자세한 내용을 읽어볼 수 있습니다.

state를 초기화할 때, 여러분은 초기 document를 전달할 수 있습니다. 이러한 경우 schema 필드는 옵션입니다. 왜냐하면 스키마는 document로부터 가져올 수 있거든요.

이제 DOM parser 메커니즘을 이용하여 "content"라는 ID를 가진 DOM element에서 발견되는 내용을 파싱하여 state를 초기화해봅시다. DOM parser 메커니즘은 어떤 DOM 노드가 어떤 element에 대응하는지에 대해 스키마로부터 공급받은 정보를 이용합니다.

import {DOMParser} from "prosemirror-model"
import {EditorState} from "prosemirror-state"
import {schema} from "prosemirror-schema-basic"

let content = document.getElementById("content")
let state = EditorState.create({
  doc: DOMParser.fromSchema(schema).parse(content)
})
profile
데이터 사이언스 공부중

0개의 댓글