"decoration 엔진이 세 역할을 한다는 건 덤이 아니야. CodeMirror 를 고른 이유 그 자체지."
세 기능, 하나의 원시 요소
한 발 물러나서 세 역할을 나란히 놓고 봐. Live Preview 는 범위를 계산해서 mark 를 붙여. voice underline 은 범위를 계산해서 mark 와 widget 을 붙이고. CMD+K 는 제안을 담은 widget 을 붙였다가 수락할 때 문서를 바꿔. 겉보기엔 아무 상관 없는 세 기능, 그러니까 렌더러와 linter 와 AI 에디터가 같은 모양으로 정리되는 거야. 소스 위의 범위를 계산하고, decoration 을 붙이고, 사용자가 확정할 때만 문서를 바꾼다. 원시 요소 하나를 한 번 익히면 soul 레이어 전체가 그 위에서 굴러가.
Live Preview 범위 -> mark (모습 렌더)
voice underline 범위 -> mark + 여백 widget (voice 이슈 표시)
CMD+K widget 제안 -> 수락 편집 하나 (AI 재작성 무대)
# 입력만 다르고 decoration 원시 요소는 같아. 그게 이 선택의 승리야.
rich-text 모델이라면 우회로 셋으로 갈라져
이제 같은 세 기능을 Tiptap 이나 Lexical 같은 rich-text 트리 위에 짓는다고 상상해봐. Live Preview 는 개념 자체가 성립을 안 해. 트리가 이미 렌더된 형태라서 '소스를 렌더된 모습으로 보여준다' 는 발상이 모델과 부딪히거든. voice underline 은 문자 범위 대신 노드 트리를 뒤져서 위치를 찾고 주석을 달아야 해. CMD+K 프리뷰는 직접 편집되기를 전제로 만들어진 문서 모델 안에서 비파괴적인 제안을 표현해야 하고. 기능마다 트리의 결을 거스르는 자기만의 우회로가 생겨. 서브시스템 하나가 아니라 셋을 짓게 되는 거지.
재사용이 곧 아키텍처
그래서 '메커니즘 하나에 역할 셋' 은 운 좋은 우연이 아니라 하중을 떠받치는 결정이야. CodeMirror 를 고른 건 소스 위에 decoration 을 얹는 그 원시 요소가 세 soul 기능을 전부 같은 모양으로 만들어주기 때문이거든. margin-Pippa 도 라이브 voice 도 inline AI 도, 제품 전체가 한 번 짓고 한 번 디버깅한 메커니즘 하나 위에 올라타. 이 가족의 첫 번째 pillar 인 Reuse 를 에디터 코어 선택으로 표현하면 이렇게 되는 거야. 기능이 제일 많은 substrate 를 고르지 마. 네 기능들이 이미 공유하고 있는 원시 요소를 가진 쪽을 골라. 재사용이 이 정도로 깊어지면 그건 더 이상 최적화가 아니라 아키텍처야.