커스텀 블록 노드 타입

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

스키마 정의

커스텀 블록 노드 타입의 스키마 정의:

{
  customBlock: {
    content: 'block*',
    group: 'block',
    attrs: {
      type: { default: '' },
      data: { default: '{}' }
    },
  }
}

모델 표현

모델 표현 예제:

{
  type: 'customBlock',
  attrs: {
    type: 'my-custom-block',
    data: JSON.stringify({ custom: 'data' })
  },
  children: []
}

HTML 직렬화

모델을 HTML로 변환:

function serializeCustomBlock(node) {
  return '<div data-type="custom-block" data-block-type="' + node.attrs.type + 
         '" data-block-data="' + escapeHtml(node.attrs.data) + '">' + 
         serializeChildren(node.children) + '</div>';
}

HTML 역직렬화

HTML을 모델로 파싱:

function parseCustomBlock(domNode) {
  return {
    type: 'customBlock',
    attrs: {
      type: domNode.getAttribute('data-block-type') || '',
      data: domNode.getAttribute('data-block-data') || '{}'
    },
    children: Array.from(domNode.childNodes)
      .map(node => parseNode(node))
      .filter(Boolean)
  };
}

뷰 연동

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

뷰 연동 코드:

// 커스텀 블록 렌더링
const customBlock = document.createElement('div');
customBlock.setAttribute('data-type', 'custom-block');
customBlock.setAttribute('data-block-type', node.attrs.type);
customBlock.setAttribute('data-block-data', node.attrs.data);
customBlock.contentEditable = 'true';
node.children.forEach(child => {
  customBlock.appendChild(renderNode(child));
});

일반적인 문제

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

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

// 문제: 커스텀 블록 타입 등록
// 해결: 커스텀 블록 렌더러 등록
const customBlockRenderers = {
  'my-custom-block': (node, data) => {
    // 커스텀 렌더링 로직
  }
};

// 문제: 커스텀 블록 데이터 검증
// 해결: 커스텀 블록 데이터 검증
function validateCustomBlockData(data, type) {
  // 블록 타입에 따라 검증
  return true;
}

구현

완전한 구현 예제:

class CustomBlockNode {
  constructor(attrs, children) {
    this.type = 'customBlock';
    this.attrs = {
      type: attrs?.type || '',
      data: attrs?.data || '{}'
    };
    this.children = children || [];
  }
  
  toDOM() {
    const customBlock = document.createElement('div');
    customBlock.setAttribute('data-type', 'custom-block');
    customBlock.setAttribute('data-block-type', this.attrs.type);
    customBlock.setAttribute('data-block-data', this.attrs.data);
    this.children.forEach(child => {
      customBlock.appendChild(child.toDOM());
    });
    return customBlock;
  }
  
  static fromDOM(domNode) {
    return new CustomBlockNode(
      {
        type: domNode.getAttribute('data-block-type') || '',
        data: domNode.getAttribute('data-block-data') || '{}'
      },
      Array.from(domNode.childNodes)
        .map(node => parseNode(node))
        .filter(Boolean)
    );
  }
}