"맞춤법 검사기는 사전을 알고, voice 검사기는 너를 알아. Rekindle 의 두 번째 decoration 역할이 뒤쪽이야."
역할 둘 — 맞춤법 검사기가 아니라 voice 검사기
decoration 엔진의 두 번째 역할은 세상 어디에도 없는 물건이야. 라이브 voice 검사기. 글을 쓰는 동안 voice 규칙에 걸리는 구절에 밑줄을 긋고 왜 걸렸는지 설명하는 작은 여백 노트를 띄워. register 가 미끄러졌거나, token-mirror 함정에 빠졌거나, 단어가 지나치게 셀 때 같은 경우지. 맞춤법 검사기는 네 텍스트를 모두가 공유하는 사전에 맞춰 보지만, voice 검사기는 네 독트린에 맞춰 봐. 한 작가가 중요하다고 정한 함정과 선호 말이야. 밑줄을 제자리에 긋는 UX 는 맞춤법 검사와 같은데, 겨누는 대상이 지극히 사적인 거지.
독트린의 실제 예를 하나 들면, 문서로 정리된 한국어 규칙 중에 박다 를 잡아내는 게 있어. 더 부드러운 표현이 맞는 자리에 지나치게 센 동사가 들어갔을 때 표시하는 거지. 문법 오류가 아니라서 어떤 범용 도구도 절대 못 잡아. 이건 voice 규칙이고, 한 작가의 독트린을 중심으로 지은 voice 검사기만 지켜낼 수 있어.
탐지는 경계 하나로 묶는다
메커니즘으로 보면 mark 에 widget 을 더한 거야. 문제가 된 범위에 밑줄을 긋고 (mark) 그 옆에 노트를 붙이면 (widget) 끝이지. 지능은 전부 detectVoiceIssues(doc) 라는 함수 하나 뒤에 숨어 있어. 이 함수는 문자 범위와 노트를 돌려주기만 해. 지금은 TypeScript 로 짠 결정론적 규칙이 탐지를 맡지만, 나중에 Rust voice 엔진으로 옮기거나 AI 판단 레이어를 키워도 decoration 코드는 그대로야. 범위만 받아 쓰니까. 요령은 전부 이 경계에 있어. '뭐가 문제인가' 를 함수 하나 뒤에 가둬두면 '어떻게 그릴 것인가' 는 탐지가 어떻게 진화하든 그대로 남거든.
type VoiceIssue = { from: number; to: number; note: string };
// 탐지는 경계 하나로 묶여 있어. 지금은 규칙이고 나중엔 Rust 나 AI 판단일 수도.
// decoration 레이어는 그대로야. 범위만 받아 쓰니까.
function detectVoiceIssues(doc: string): VoiceIssue[] { /* ... */ }
같은 엔진, 새 목적
한 발 물러나서 보면, 이건 Live Preview 의 기계장치를 그대로 두고 겨누는 질문만 바꾼 거야. Live Preview 는 '마크업이 어디 있지' 를 묻고 mark 를 그렸어. voice underline 은 'voice 이슈가 어디 있지' 를 묻고 mark 에 노트를 더해 그리고. 원시 요소는 같고 입력만 새로운 거지. 이 트랙의 thesis 가 두 번째로 확인되는 지점이고, 세상 어디에도 없는 voice 검사기라는 진짜 새 기능이 새 기계장치를 거의 안 들이고 지어진 이유이기도 해.