개요
WASM “뇌”를 쓰는 웹 에디터도 대개 브라우저 편집 표면을 씁니다: 루트 contenteditable(또는 얇은 래퍼). Rust는 검증·변환·협업·무거운
파싱을 맡고, DOM은 캐럿·IME·네이티브 선택을 맡습니다.
이 페이지는 그 분업을 건강하게 유지하는 정책·루프를 다룹니다. 일반적인 모델/뷰 이론은 에디터 → 아키텍처, IME·클립보드 세부는 연결된 Wasm 가이드를 보세요.
contenteditable + WASM 하이브리드
하이브리드에서는 JavaScript/TypeScript가 사용자가 입력하는 DOM 트리에 리스너를 달고, 필요 시 Rust에서 돌아온 패치를 적용합니다. WASM은 실행 취소·협업·내보내기에 믿을 수 있는 정본 문서(또는 연산 로그)를 가집니다.
- 장점: IME·맞춤법·접근성 트리·플랫폼 선택 동작을 브라우저가 제공합니다.
- 단점: 브라우저마다 HTML 조금씩 다르게 만듭니다. 지속적으로 맞춰야 하며, 시나리오에 실제 차이가 정리돼 있습니다.
Rust는 여기서 편집 표면 전체를 대체하지 않고, 한 언어로 두고 싶은 깨지기 쉽거나 비싼 문서 로직을 옮기는 쪽입니다.
진실의 원천
한 시점에 하나의 권위를 정하세요:
- 모델 우선: 안정된 편집마다(예: IME
compositionend후) WASM에 연산 적용 → JS가 DOM을 모델에 맞춤. 조합 중에 DOM과 싸우면 위험합니다. - 입력 중 DOM 우선: 타이핑 동안은 DOM이 정본이고, 주기적으로 또는 커밋 시점에 Rust 모델로 파싱. 파싱이 손실이 있으면 스키마·테스트로 틈을 막아야 합니다.
실용적 절충: IME 조합 세션 동안은 DOM을 우선하고, 확정 후 한 번에 Rust에 반영하는 패턴이 많습니다. IME·조합 입력.
DOM 전체 문자열과 Rust 트리를 매 키마다 서로 독립적으로 갱신하면 레이스·중복 문자가 납니다. 넘기는 지점을 설계하세요.
입력 이벤트·순서
컨트롤러 코드 안에 명시적 파이프라인을 두세요(핸들러만 흩뿌리지 않기):
-
beforeinput— 가능한 편집 가로채기/취소,getTargetRanges()활용. -
input— DOM 변경 후. 조합 밖에서 DOM 스냅샷 동기화를 둘 때 디바운스. -
composition*— Rust에 “미리보기”를 줄지, 확정 텍스트만 줄지. -
selectionchange— 모델 좌표로 매핑; 비동기 WASM 갱신이 Range를 깨뜨리지 않게. 선택·오프셋.
이벤트 의미: 에디터 → 입력 처리.
DOM ↔ 모델 루프
흔한 루프 예:
- 커밋 시 파싱: 서브트리 직렬화 또는 DOM diff → 구조화 연산을 WASM에 → 모델이 거부·정규화하면 DOM에 반영.
- JS에서 연산 스트림:
beforeinput유형을 작은 연산으로 바꿔 WASM에 적용 → 최소 DOM 패치 지시를 반환. - 투영: WASM이 권위; 트랜잭션마다 직렬화 뷰로 DOM 전체 또는 부분 교체(제어는 크고 diff 비용도 큼).
매 이벤트마다 HTML 전체를 WASM으로 넘기지 말고 프로파일로 검증하세요. JS ↔ WASM 경계.
이론: 에디터 → 모델–DOM 동기화.
DOM 형태·스키마
브라우저는 <br>, 빈 블록, 중첩 span을 제각각 넣습니다. Rust 스키마로 허용 형태를
정하고, Enter·붙여넣기 뒤에 정규화합니다(JS에서 빠른 DOM 수술 또는
WASM에서 네이티브와 규칙 공유).
- 붙여넣기 후·CRDT에 넣기 전에 금지 태그를 걸러내고 정책을 하나로 유지. 클립보드·입력 라우팅.
- 보안·배포와 같이 XSS 정책이 갈라지지 않게 하세요.
스키마·HTML: 에디터 → 모델·스키마, HTML 매핑.
비편집 아일랜드
contenteditable=false·멘션·수식 블록·위젯을 본문에 섞으면
경계에서 선택이 접히거나 점프합니다. Android와 데스크톱 차이도 큽니다.
WASM이 해결해 주지 않습니다. 뷰 레이어에서 클릭 대상·키보드 포커스·Rust 모델로의 매핑(위젯 건너뛰기)을 정해야 합니다.
접근성, 비편집 블록 관련 케이스는 사이트 시나리오를 참고하세요.
WASM 호출 시점
실무용 가이드:
- 호출: 스키마 검증, 변환 적용, 붙여넣기 HTML 파싱, CRDT 병합, 내보내기/가져오기, 무거운 diff.
- 피하기: 모델 미리보기가 꼭 필요하지 않다면 매
compositionupdate마다 호출하지 않기. - 배치: 빠른 입력을 프레임 단위 등으로 묶되, IME 커밋 순서를 뒤바꾸지 않기.
캔버스·자체 렌더러
본문을 Canvas/WebGL로 그리는 제품도 숨긴 contenteditable로
IME를 받는 경우가 있습니다. 완전 커스텀이면 IME·접근성 부담이 큽니다.
인쇄 정밀 레이아웃·게임 UI 같은 명시적 이유가 없으면 DOM 호스트를 유지하는 편이 낫습니다.
Rust가 맡는 일
- 문서 모델 + 연산(변환 적용, 스키마 강제).
- 붙여넣기·가져오기 HTML 파싱, 마크다운 왕복.
- CRDT·동기화(예: Yrs)를 WASM으로 두고 JS 호스트와 연동. 협업·CRDT.
- 본문 줄바꿈을 브라우저에 안 맡기는 고급 레이아웃(선택).
선택·다음 단계
기본 권장: contenteditable 표면 + WASM 코어, 진실 원천 규칙 명시, 디버거에서 추적 가능한 동기화 루프. 렌더 최적화 전에 경계 트래픽을 측정하세요.
다음 읽을 것: IME·조합 입력 · JS ↔ WASM 경계 · 실행 취소·다시 실행 모델
Wasm 가이드
IME·조합 입력
조합 이벤트, Rust 문서 모델 동기화, IME는 여전히 브라우저 영역인 이유.
JS ↔ WASM 경계
문자열·복사·배치 연산, 비동기와 입력 이벤트, 핫 패스 비용 줄이기.
클립보드·입력 라우팅
beforeinput, 붙여넣기, JS에서 라우팅할지 WASM에서 정제할지.
툴링·번들·워커
wasm-pack, wasm-opt, 코드 스플릿, Web Worker, COOP/COEP·스레드.
협업·CRDT (WASM)
Yrs·y-crdt, Yjs 연동, 스냅샷 vs 업데이트 스트림.
선택·Range·오프셋
UTF-16 vs UTF-8, JS Selection/Range, Rust 모델 매핑·getTargetRanges.
실행 취소·다시 실행 모델
브라우저 undo 스택 vs 모델 히스토리, 프로그래밍 DOM, WASM 트랜잭션.
접근성 (WASM 호스트)
편집 표면이 브라우저일 때 역할·포커스·스크린 리더.
테스트·디버깅
E2E, JS↔WASM 경계 프로파일링, IME·붙여넣기 CI 재현.
보안·배포
CSP, SRI, 모듈 무결성, contenteditable 옆 WASM 호스팅.