Skip to content

Latest commit

Β 

History

History
116 lines (90 loc) Β· 6.64 KB

custom-block.md

File metadata and controls

116 lines (90 loc) Β· 6.64 KB

πŸ”© μ»€μŠ€ν…€ 블둝 λ…Έλ“œμ™€ HTML λ…Έλ“œ

TOAST UI Editor(μ΄ν•˜ '에디터'라고 λͺ…μ‹œ)λŠ” CommonMark μŠ€νŽ™μ„ μ€€μˆ˜ν•˜λ©°, μΆ”κ°€λ‘œ GFM μŠ€νŽ™λ„ μ§€μ›ν•œλ‹€. ν•˜μ§€λ§Œ λ§Œμ•½ CommonMarkλ‚˜ GFMμ—μ„œ μ§€μ›ν•˜μ§€ μ•ŠλŠ” νŠΉμ • 문법을 μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ μ–΄λ–¨κΉŒ? 예λ₯Ό λ“€μ–΄ λ§ˆν¬λ‹€μš΄μ—μ„œ LaTeX 문법을 μ‚¬μš©ν•˜κ±°λ‚˜ 차트 같은 μš”μ†Œλ₯Ό λ Œλ”λ§ν•˜κ³  싢을 수 μžˆλ‹€. μ—λ””ν„°μ—μ„œλŠ” μ΄λŸ¬ν•œ μ‚¬μš©μ„±μ„ μœ„ν•΄ μ‚¬μš©μžλ§Œμ˜ μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ₯Ό μ •μ˜ν•  수 μžˆλŠ” μ˜΅μ…˜μ„ μ œκ³΅ν•œλ‹€.

μ»€μŠ€ν…€ 블둝 λ…Έλ“œ

μ—λ””ν„°λŠ” λ§ˆν¬λ‹€μš΄ AST(Abstract Syntax Tree)λ₯Ό HTML λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•  λ•Œ μ»€μŠ€ν„°λ§ˆμ΄μ§•ν•  수 μžˆλŠ” customHTMLRenderer μ˜΅μ…˜μ„ μ œκ³΅ν•œλ‹€. customHTMLRenderer μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄ table, heading처럼 CommonMarkλ‚˜ GFMμ—μ„œ μ§€μ›ν•˜λŠ” λ…Έλ“œμ˜ λ Œλ”λ§ κ²°κ³Όλ₯Ό μ»€μŠ€ν„°λ§ˆμ΄μ§•ν•  수 μžˆλ‹€. μ»€μŠ€ν…€ 블둝 λ…Έλ“œ μ—­μ‹œ 이 customHTMLRenderer μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ •μ˜ν•  수 μžˆλ‹€.

λ‹€μŒ μ½”λ“œλŠ” LaTex 문법을 μ§€μ›ν•˜λŠ” 라이브러리인 KaTeXλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆ˜μ‹μ„ λ Œλ”λ§ν•˜λŠ” μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ₯Ό μ •μ˜ν•œ 것이닀.

const editor = new Editor({
  el: document.querySelector('#editor'),
  customHTMLRenderer: {
    latex(node) {
      const generator = new latexjs.HtmlGenerator({ hyphenate: false });
      const { body } = latexjs.parse(node.literal, { generator }).htmlDocument();

      return [
        { type: 'openTag', tagName: 'div', outerNewLine: true },
        { type: 'html', content: body.innerHTML },
        { type: 'closeTag', tagName: 'div', outerNewLine: true }
      ];
    },
  }
});

customHTMLRenderer μ˜΅μ…˜μ— latex ν•¨μˆ˜ ν”„λ‘œνΌν‹°λ₯Ό μž‘μ„±ν•˜μ˜€κ³  이 ν•¨μˆ˜μ—μ„œλŠ” λ Œλ”λ§ 될 HTML을 토큰 ν˜•νƒœλ‘œ λ°˜ν™˜ν•œλ‹€. λ§ˆν¬λ‹€μš΄ λ…Έλ“œλ₯Ό μ»€μŠ€ν„°λ§ˆμ΄μ§•μ„ ν•  λ•Œμ™€ 거의 λ™μΌν•œ ν˜•νƒœλ‘œ μ˜΅μ…˜μ„ μ§€μ •ν•˜κΈ° λ•Œλ¬Έμ— μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€. μœ„μ˜ μ½”λ“œλŠ” λ§ˆν¬λ‹€μš΄ μ—λ””ν„°μ—μ„œ λ‹€μŒμ²˜λŸΌ λ Œλ”λ§λœλ‹€.

image

μœ„μ˜ μ΄λ―Έμ§€μ—μ„œ λ³Ό 수 μžˆλ“―μ΄ λ§ˆν¬λ‹€μš΄ μ—λ””ν„°μ—μ„œ μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” $$ 기호둜 감싸진 블둝 내에 ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•΄μ•Ό ν•œλ‹€. $$ 기호둜 감싸진 블둝은 μ—λ””ν„°μ—μ„œ μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ‘œ νŒŒμ‹±λœλ‹€. λ˜ν•œ μ–΄λ– ν•œ μ»€μŠ€ν…€ 블둝 λ…Έλ“œμΈμ§€ λ‚˜νƒ€λ‚΄κΈ° μœ„ν•΄ $$ 기호 λ‹€μŒμ— λ°˜λ“œμ‹œ customHTMLRenderer μ˜΅μ…˜μ—μ„œ μ •μ˜ν•œ λ…Έλ“œ 이름을 μž‘μ„±ν•΄μ•Ό ν•œλ‹€.

// $$ 기호 뒀에 μ˜΅μ…˜μ—μ„œ μ •μ˜ν•œ λ…Έλ“œ 이름을 λ°˜λ“œμ‹œ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.
$$latex
\documentclass{article}
\begin{document}

$
f(x) = \int_{-\infty}^\infty \hat f(\xi)\,e^{2 \pi i \xi x} \, d\xi
$
\end{document}
$$

μœ„μ§€μœ…

μ •μ˜λœ μ»€μŠ€ν…€ 블둝 λ…Έλ“œλŠ” μœ„μ§€μœ… μ—λ””ν„°μ—μ„œ μ•„λž˜ μ΄λ―Έμ§€μ²˜λŸΌ λ™μž‘ν•œλ‹€.

image

μœ„μ§€μœ… μ—λ””ν„°μ—μ„œ μ»€μŠ€ν…€ 블둝 λ…Έλ“œλŠ” λ§ˆν¬λ‹€μš΄ 프리뷰와 λ™μΌν•œ λͺ¨μŠ΅μœΌλ‘œ λ Œλ”λ§λ˜λ©°, λ…Έλ“œλ₯Ό ν΄λ¦­ν•˜μ—¬ μ„ νƒν–ˆμ„ λ•Œ λ‚˜μ˜€λŠ” νŽΈμ§‘ λ²„νŠΌμ„ 톡해 λ‚΄μš©μ„ λ³€κ²½ν•  수 μžˆλ‹€. μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ„ κ²°κ΅­ νŠΉμ • ν…μŠ€νŠΈλ₯Ό κΈ°μ€€μœΌλ‘œ νŒŒμ‹±λ˜λŠ” 것이기 λ•Œλ¬Έμ— μœ„μ§€μœ… μ—λ””ν„°μ—μ„œμ˜ νŽΈμ§‘λ„ ν…μŠ€νŠΈλ₯Ό κΈ°μ€€μœΌλ‘œ ν•œλ‹€. μ΄λŠ” 일반적인 μœ„μ§€μœ… μ—λ””ν„°μ™€λŠ” λ‹€λ₯Έ λ™μž‘μ΄μ§€λ§Œ TOAST UI EditorλŠ” λ§ˆν¬λ‹€μš΄μ„ 기반으둜 μœ„μ§€μœ… 에디터λ₯Ό μ§€μ›ν•˜κΈ° λ•Œλ¬Έμ— μ΄λŸ¬ν•œ λ™μž‘μ΄ 더 이상적이닀.

HTML λ…Έλ“œ

CommonMarkμ—μ„œλŠ” <κ³Ό > 문자λ₯Ό μ‚¬μš©ν•˜μ—¬ 기본적으둜 μ§€μ›ν•˜μ§€ μ•ŠλŠ” λ…Έλ“œλ₯Ό HTML λ¬Έμžμ—΄ ν˜•νƒœλ‘œ μž‘μ„±ν•  수 μžˆλ‹€. (CommonMark Raw HTML Spec μ°Έμ‘°)

μ—λ””ν„°μ˜ λ§ˆν¬λ‹€μš΄ μ—λ””ν„°μ—μ„œλ„ μ΄λŸ¬ν•œ μŠ€νŽ™μ„ μ€€μˆ˜ν•˜κΈ° λ•Œλ¬Έμ— HTML λ¬Έμžμ—΄μ€ λ§ˆν¬λ‹€μš΄ ν”„λ¦¬λ·°μ—μ„œ μ˜¬λ°”λ₯΄κ²Œ λ Œλ”λ§ λœλ‹€.

image

μœ„μ§€μœ…

ν•˜μ§€λ§Œ μ•ˆνƒ€κΉκ²Œλ„ μœ„μ§€μœ… μ—λ””ν„°μ—μ„œλŠ” HTML λ…Έλ“œλ₯Ό μ œλŒ€λ‘œ λ Œλ”λ§ν•  수 μ—†λ‹€. μ—λ””ν„°λŠ” λ‚΄λΆ€μ μœΌλ‘œ μœ„μ§€μœ… μ—λ””ν„°μ—μ„œ 기본으둜 μ§€μ›ν•˜λŠ” λ…Έλ“œλ₯Ό μΆ”μƒν™”λœ λͺ¨λΈ 객체둜 κ΄€λ¦¬ν•˜κ³  μžˆλ‹€. μœ„μ§€μœ… μ—λ””ν„°μ—μ„œ μ§€μ›ν•˜λŠ” λ…Έλ“œλž€ CommonMark와 GFM μ—μ„œ μ§€μ›ν•˜λŠ” λ…Έλ“œ(heading, list, strike λ“±)와 μ»€μŠ€ν…€ 블둝 λ…Έλ“œλ₯Ό μ˜λ―Έν•œλ‹€.

image

μœ„ μ˜ˆμ‹œ μ΄λ―Έμ§€μ˜ iframe λ…Έλ“œλŠ” μœ„μ§€μœ… μ—λ””ν„°μ—μ„œ 기본적으둜 μ§€μ›ν•˜λŠ” λ…Έλ“œκ°€ μ•„λ‹ˆλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— iframe λ…Έλ“œλ₯Ό μœ„μ§€μœ… μ—λ””ν„°μ—μ„œλ„ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ customHTMLRenderer μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μΆ”κ°€ 섀정을 ν•΄μ•Ό ν•œλ‹€.

const editor = new Editor({
  el: document.querySelector('#editor'),
  customHTMLRenderer: {
    htmlBlock: {
      iframe(node) {
        return [
          { type: 'openTag', tagName: 'iframe', outerNewLine: true, attributes: node.attrs },
          { type: 'html', content: node.childrenHTML },
          { type: 'closeTag', tagName: 'iframe', outerNewLine: true },
        ];
      },
    }
  },
});

HTML λ…Έλ“œλŠ” customHTMLRenderer.htmlBlock ν”„λ‘œνΌν‹°μ— μ •μ˜ν•œλ‹€. μœ„μ—μ„œ μ„€λͺ…ν•œ μ»€μŠ€ν…€ 블둝 λ…Έλ“œμ™€ κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄ htmlBlock ν”„λ‘œνΌν‹° λ‚΄μ—μ„œ μΆ”κ°€ν•  HTML λ…Έλ“œμ˜ μ»¨λ²„νŒ… ν•¨μˆ˜λ₯Ό μ •μ˜ν•œλ‹€. 예제 μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜ μ΄λ―Έμ§€μ²˜λŸΌ μœ„μ§€μœ…μ—μ„œλ„ iframe λ…Έλ“œκ°€ μ˜¬λ°”λ₯΄κ²Œ λ Œλ”λ§λœλ‹€.

image

λ§Œμ•½ 인라인 HTML λ…Έλ“œλ₯Ό μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄, customHTMLRenderer.htmlInline ν”„λ‘œνΌν‹°μ— μ •μ˜ν•œλ‹€.

const editor = new Editor({
  el: document.querySelector('#editor'),
  customHTMLRenderer: {
    htmlBlock: {
      iframe(node) {
        return [
          { type: 'openTag', tagName: 'iframe', outerNewLine: true, attributes: node.attrs },
          { type: 'html', content: node.childrenHTML },
          { type: 'closeTag', tagName: 'iframe', outerNewLine: true },
        ];
      },
    },
    htmlInline: {
      big(node, { entering }) {
        return entering
          ? { type: 'openTag', tagName: 'big', attributes: node.attrs }
          : { type: 'closeTag', tagName: 'big' };
      },
    },
  },
});