코드 노드 타입

카테고리: 포맷팅 • 뷰 연동 노트를 포함한 상세 구현 가이드

스키마 정의

코드 마크 타입의 스키마 정의:

{
  code: {
    excludes: 'bold italic underline link'
    // 코드 마크는 다른 포맷팅 마크를 배제함
  }
}

모델 표현

모델 표현 예제:

{
  type: 'text',
  text: '인라인 코드',
  marks: [{ type: 'code' }]
}

HTML 직렬화

모델을 HTML로 변환:

function serializeCodeMark(text, mark) {
  return '<code>' + escapeHtml(text) + '</code>';
}

HTML 역직렬화

HTML을 모델로 파싱:

function extractCodeMark(element) {
  if (element.tagName === 'CODE') {
    return { type: 'code' };
  }
  return null;
}

뷰 연동

뷰 연동 노트: 이 노드 타입을 뷰 레이어에서 구현할 때 contenteditable 동작, 선택 처리, 이벤트 관리에 특히 주의하세요.

뷰 연동 코드:

// 코드 토글
function toggleCode() {
  const selection = window.getSelection();
  if (!selection.rangeCount) return;
  
  const range = selection.getRangeAt(0);
  const hasCode = hasMarkInSelection(range, 'code');
  
  if (hasCode) {
    removeMark('code');
  } else {
    // 먼저 충돌하는 마크 제거
    removeConflictingMarks(['bold', 'italic', 'underline', 'link']);
    addMark({ type: 'code' });
  }
}

일반적인 문제

일반적인 함정: 이 노드 타입을 구현할 때 자주 발생하는 문제들입니다. 구현 전에 주의 깊게 검토하세요.

일반적인 문제 및 해결 방법:

// 문제: 코드 마크가 다른 마크를 배제함
// 해결: 코드 적용 시 충돌하는 마크 제거
function applyCodeMark(textNode) {
  // 볼드, 이탤릭, 밑줄, 링크 제거
  const conflictingMarks = ['bold', 'italic', 'underline', 'link'];
  conflictingMarks.forEach(mark => {
    removeMark(mark);
  });
  addMark({ type: 'code' });
}

// 문제: 코드 vs 코드 블록
// 해결: 인라인 코드와 코드 블록 구분
function isCodeBlock(element) {
  return element.tagName === 'PRE' || 
         (element.tagName === 'CODE' && element.parentElement?.tagName === 'PRE');
}

// 문제: 코드 마크와 특수 문자
// 해결: 코드에서 HTML 이스케이프
function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

구현

완전한 구현 예제:

class CodeMark {
  constructor() {
    this.type = 'code';
  }
  
  toDOM() {
    return ['code', 0];
  }
  
  static fromDOM(element) {
    if (element.tagName === 'CODE' && 
        element.parentElement?.tagName !== 'PRE') {
      return new CodeMark();
    }
    return null;
  }
}