# 마크다운 확장 기능에 리치 텍스트 에디터 지원을 추가하는 방법은?

**URL:** https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753
**Category:** Development
**Created:** [11월 6, 2025, 7:32오전 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753 "2025-11-06T07:32:30Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![ego-lay\_atman-bay](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ego-lay_atman-bay/32/255458_2.png) [@ego-lay\_atman-bay](https://meta.discourse.org/u/ego-lay_atman-bay)
#### Post date: [11월 6, 2025, 7:32오전 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/1 "2025-11-06T07:32:30Z")

</div>

나 [snapblocks](https://snap-blocks.github.io/)를 렌더링하기 위해 몇 가지 bbcode 태그를 추가하는 [확장 기능](https://meta.discourse.org/t/snapblocks/310155?u=ego-lay_atman-bay)을 만들었습니다. 리치 텍스트 에디터가 도입된 이후 이 확장 기능이 약간 깨지거나, 적어도 리치 텍스트 에디터와 함께 사용할 수 없는 상태가 되었습니다. 리치 텍스트 에디터 지원을 추가하기 위해 spoiled 확장 기능을 조사해 보니, 현재는 어느 정도 작동하는 상태로 만들었습니다. 실제로 리치 텍스트 에디터에서 게시물에 snapblocks를 삽입할 수는 있지만, 기존 snapblocks 태그를 리치 텍스트 에디터 형식으로 변환하는 방법을 찾지 못하고 있습니다.

지금까지 만든 내용은 다음과 같습니다.

> <https://github.com/snap-blocks/snapblocks-discourse/blob/rich-text-support/assets/javascripts/lib/rich-editor-extension.js>

현재 저는 실제 변환 과정에 집중하고 있습니다. 작동 방식에 대한 기본적인 추측은 있지만, 아직까지 결과를 얻지 못하고 있습니다. 그래서 파싱을 작동시키는 방법은 무엇일까요?

---

<div class="post-metadata">

### Author: ![ego-lay\_atman-bay](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ego-lay_atman-bay/32/255458_2.png) [@ego-lay\_atman-bay](https://meta.discourse.org/u/ego-lay_atman-bay)
#### Post date: [11월 9, 2025, 6:37오전 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/2 "2025-11-09T06:37:40Z")

</div>

머리를 쥐어뜯고 discourse에 `console.log()`를 많이 추가한 끝에, 왜 작동하지 않았는지 그리고 어떻게 작동하게 만들 수 있는지 발견했습니다.

파싱은 렌더링된 HTML이 아니라 markdownit 토큰으로 수행됩니다. 저는 `[snapblocks]` bbcode 태그를 렌더링하기 위해 `replace()` 함수에서 `state.push(html_raw)`를 사용하고 있었습니다.

```javascript
// assets/javascripts/lib/discourse-markdown/snapblocks-discourse.js

export function(helper) {
  md.block.bbcode.ruler.push("snapblocks", {
      tag: "snapblocks",
      replace(state, tagInfo, content) {
        let token = state.push('html_raw', '', 0)
        token.content = `<pre class="snapblocks-blocks">${content}</pre>`
      },
    });
}

```

문제는 `html_raw` 토큰 유형이 리치 텍스트 에디터 변환기에 의해 무시된다는 점입니다. 즉, 리치 텍스트 에디터 지원을 원한다면 이 방법을 사용할 수 없습니다.

`bbcode_open`, `text`, `bbcode_close`를 사용하면 작동하게 된다는 것을 발견했습니다.

```javascript
// assets/javascripts/lib/discourse-markdown/snapblocks-discourse.js

export function(helper) {
  md.block.bbcode.ruler.push("snapblocks", {
      tag: "snapblocks",
      replace(state, tagInfo, content) {
        let token = state.push('bbcode_open', 'pre', 1)
        token.attrs = [['class', 'snapblocks-discourse']]

        token = state.push('text', '', 0)
        token.content = content

        token = token.push('bbcode_close', 'pre', -1)
        token.attrs = [['class', 'snapblocks-discourse']] // 나중에 검사하는 데 필요
      },
    });
}

```

이렇게 하면 더 이상 무시되지 않습니다.

`rich-text-editor-extension.js`로 이동하여 다음을 수행합니다.

```javascript
// assets/javascripts/lib/rich-text-editor-extension.js

import { i18n } from "discourse-i18n";

const SNAPBLOCKS_NODES = ["inline_snapblocks", "snapblocks"];

/** @type {RichEditorExtension} */
const extension = {
    snapblocks: {
      attrs: { rendered: { default: true } },
      code: true,
      group: "block",
      content: "text*", // 이것은 필요합니다
      createGapCursor: true,
      parseDOM: [{ tag: "pre.snapblocks-blocks" }],
      toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
    },
  },
  parse: {
    bbcode_open(state, token) {
      // 여기서 토큰은 이전 코드와 동일한 `bbcode_open` 토큰 객체입니다
      if (token.attrGet('class') === 'snapblocks-blocks') {
        state.openNode(state.schema.nodes.snapblocks, {
          open: token.attrGet("open") !== null,
        });
        return true;
      }
    },
    bbcode_close(state, token) {
      if (token.attrGet('class') === 'snapblocks-blocks') {
        state.closeNode();
        return true;
      }
    },
  },
  serializeNode: {
    snapblocks(state, node) {
      // 이것은 단순히 일반 텍스트 마크다운으로 변환합니다
      state.write("[snapblocks]\n");
      state.renderContent(node);
      state.write("\n[/snapblocks]\n\n");
    },
  },
}

```

`parse` 속성의 키들은 마크다운 파서에서 사용할 수 있는 토큰 유형에 대한 파서입니다.

이것보다 훨씬 더 많은 것이 있지만, 적어도 이것으로 상황이 훨씬 명확해질 것이며, 제가 해야 할 머리를 쥐어뜯는 횟수가 줄어들 것입니다.

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [11월 9, 2025, 12:46오후 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/3 "2025-11-09T12:46:31Z")

</div>

> [@ego-lay\_atman-bay](#):
>
> 문제는 `html_raw` 토큰 유형이 리치 텍스트 에디터 변환기에 의해 무시된다는 것이었습니다.

완전히 정확하지는 않지만, [`html-block`](https://github.com/discourse/discourse/blob/main/frontend/discourse/app/static/prosemirror/extensions/html-block.js) 확장 기능이 이미 `html_raw` markdown-it 토큰을 처리하고 있으며, 이는 _passthrough_ 콘텐츠를 처리하는 일반적인 방법입니다.

`registerRichEditorExtension`을 통해 허용되는 모든 확장 [유형](https://github.com/discourse/discourse/blob/54723610355e58514646d6162e8a2511b160738b/frontend/discourse/app/lib/composer/rich-editor-extensions.js#L74-L99)을 확인해 볼 수 있으며, [플러그인에서 제공하는 확장 기능](https://github.com/search?q=repo%3Adiscourse%2Fdiscourse%20registerRichEditorExtension&type=code)과 [기본적으로 등록된 확장 기능](https://github.com/discourse/discourse/blob/main/frontend/discourse/app/static/prosemirror/extensions/register-default.js)을 참고할 수 있습니다.

질문이 있으시면 알려주세요.

---

<div class="post-metadata">

### Author: ![ego-lay\_atman-bay](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ego-lay_atman-bay/32/255458_2.png) [@ego-lay\_atman-bay](https://meta.discourse.org/u/ego-lay_atman-bay)
#### Post date: [11월 10, 2025, 4:13오전 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/4 "2025-11-10T04:13:06Z")

</div>

> [@renato](#):
>
> 완전히 그렇지 않지만, 이미 [`html-block`](https://github.com/discourse/discourse/blob/main/frontend/discourse/app/static/prosemirror/extensions/html-block.js) 확장 기능이 `html_raw` markdown-it 토큰을 처리하고 있으며, 이는 _패스스루(passthrough)_ 콘텐츠를 처리하는 일반적인 방법입니다.

정리해 주셔서 감사합니다.

* * *

이제 블록 버전 토글이 작동하지 않는 문제에 직면했습니다. 인라인 블록은 토글할 수 있지만, 블록 레벨 노드는 토글할 수 없습니다.

어느 시점에 커서가 블록에 있지 않을 때 블록을 렌더링하도록 하고, 스타일 선택기도 추가하고 싶지만, 일단 기본 편집 기능이 정상적으로 작동하는 것이 먼저라고 생각합니다.

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [11월 10, 2025, 1:58오후 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/5 "2025-11-10T13:58:55Z")

</div>

> [@ego-lay\_atman-bay](#):
>
> 지금 블록 버전 토글링이 작동하지 않는 문제를 겪고 있습니다. 인라인 블록은 토글할 수 있지만 블록 레벨 노드는 토글할 수 없습니다.

죄송하지만 잘 이해가 되지 않습니다. “블록 버전 토글링”이 정확히 무엇을 의미하는지 알려주실 수 있을까요?

---

<div class="post-metadata">

### Author: ![ego-lay\_atman-bay](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ego-lay_atman-bay/32/255458_2.png) [@ego-lay\_atman-bay](https://meta.discourse.org/u/ego-lay_atman-bay)
#### Post date: [11월 10, 2025, 2:23오후 UTC](https://meta.discourse.org/t/how-do-i-add-rich-text-editor-support-to-my-markdown-extension/387753/6 "2025-11-10T14:23:10Z")

</div>

제 BBCode 태그에는 `[snapblocks]`와 `[sb]`가 있습니다. 여기서 `[sb]`는 인라인 태그이고, `[snapblocks]`는 블록 레벨 태그입니다. 툴바 버튼을 누르면 인라인 태그는 잘 토글되지만, 텍스트 블록을 선택한 상태에서 블록 레벨 태그는 토글되지 않습니다.
