ProseMirror 뜯어보기 - prosemirror-model의 fragment.ts

개발 · 2025. 8. 18.

해당 게시글은 정보 공유 보다는 개인적인 학습을 위한 게시글입니다.

Fragment의 개념

  • Fragment는 여러 Node를 순서 있는 리스트로 보관하는 불변 객체
  • 문서의 각 Node가 자식 노드를 가질 때, 그 자식들의 집합이 Fragment
  • 내부적으로 Node 배열 + size(문서 좌표 길이)를 관리

Node는 "한 개"를 표현하고, Fragment는 "여러 개"를 모아 놓는 컨테이너

2. 주요 속성

속성 설명
content readonly Mark[] - 노드 배열
size 모든 하위 노드의 총 길이( 텍스트 길이 + 구조 길이)
firstChild / lastChild 첫 번째 / 마지막 노드

3. 주요 메서드 분류

생성

  • Fragment.from(arrayOrNode)
    • Node 하나 또는 Node 배열로 Fragment 생성
  • empty
    • 빈 Fragment(싱글턴)

탐색

  • child(index) / maybeChild(index) : 특정 인덱스 노드 접근
  • findIndex(pos, round) : 문서 좌표 → (노드 인덱스, 오프셋) 변환
  • findDiffStart(other, pos) : 두 Fragment 비교 → 처음 다른 위치 반환
  • findDiffEnd(other, pos, otherPos) : 두 Fragment 비교 → 끝에서부터 처음 다른 위치 반환

활용 예: 협업 편집 시, 어떤 부분이 변경됐는지 비교


조작

  • append(other) : 두 Fragment 연결
  • cut(from, to) : 특정 범위만 잘라낸 새 Fragment
  • replaceChild(index, node) : 특정 위치 노드 교체
  • addToStart(node) / addToEnd(node) : 앞/뒤에 노드 추가
  • appendFragment(other) : 다른 Fragment 붙이기

Node 불변성 때문에, 변경은 항상 새 Fragment 반환


순회

  • forEach(f) : 모든 하위 노드 순회
  • nodesBetween(from, to, f, nodeStart, parent) : 특정 범위 모든 하위 노드 방문
  • textBetween(from, to, blockSeparator, leafText) : 범위 내 텍스트 추출

Node-Fragment 관계

  • Node content 필드로 Fragment를 가짐
  • Fragment는 그 안에 여러 Node를 저장
  • 불변 구조 → Node 변경 시, Fragment도 새로 생성 (기존 것은 유지)
const p1 = schema.node("paragraph", null, [schema.text("Hello")])
const p2 = schema.node("paragraph", null, [schema.text("World")])

const frag = Fragment.from([p1, p2])
console.log(frag.size) // 총 길이
console.log(frag.child(1).textContent) // "World"

 왜 Fragment가 중요한가?

  • 성능 최적화: Node 배열을 직접 다루지 않고, Fragment에서 비교(diff) 및 병합 관리
  • 구조 공유(Structural Sharing): 불필요한 전체 복사 없이 변경된 부분만 새로 생성
  • 트리 구조 유지: 문서 편집 시 안정적으로 부모-자식 관계 관리

정리

  • Fragment = Node들의 불변 리스트
  • Node의 content를 저장하는 컨테이너 역할
  • 생성, 탐색, 비교, 수정, 순회 메서드 제공
  • 불변성을 기반으로 Undo/Redo, 협업 편집, 효율적인 diff 연산 지원