토크나이저를 폰트 셰이핑 규칙에 심어 모든 LLM 토큰을 같은 폭으로 그리는 도구

Hacker News15일 전조회 8

LLM이 문장을 토큰으로 쪼개는 방식을 눈으로 확인할 방법은 마땅치 않다. 보통은 토크나이저 라이브러리를 직접 돌려 토큰 ID 목록을 뽑아보는 정도다. 여기서 한 발 더 나간 도구가 나왔다. 폰트 파일 자체에 토크나이저를 심어, 화면에 렌더링되는 모든 토큰이 정확히 같은 폭을 차지하도록 만드는 방식이다. 결과물은 TTF 파일 하나이고, 표시할 때 별도의 토크나이저 스크립트를 돌릴 필요가 없다.

동작 원리는 폰트의 셰이핑 규칙에 토크나이저를 내장하는 것이다. 지원되는 셰이핑 런 안에서 방출된 토큰 하나는 3em 폭을 차지한다. 추가 토큰 간격은 기본 0이고 최대 0.5em까지 늘릴 수 있다. 생성된 CSS는 단어 간격과 줄 높이를 건드리지 않는다. 폰트는 사용자가 고른 원본 폰트의 기본 배리에이션과 문자 커버리지를 그대로 쓰며, 없는 글자는 미싱 글리프나 시스템 폴백으로 넘어간다. 폴백 폰트를 켜면 선택한 폴백 윤곽선을 같은 토큰 폰트 안에 임베드한다. 이모지 폴백 기본값은 Noto Emoji이고, Mutant Standard는 별도 라이선스가 필요한 선택적 컬러 소스다. 미리 컴파일된 DeepSeek + Inter 프리뷰에는 Noto Emoji가 이미 들어 있다.

프리셋은 토크나이저별 컴파일 경로를 쓴다. DeepSeek, OpenAI, Kimi, Qwen, GLM, Llama 3, Trinity, Laguna가 여기 포함되고, Gemma와 Gemini는 실험적 공백 마커·바이트 폴백 지원 단계다. ctok은 별도의 최소 비용 백엔드를 사용한다. Raw ByteLevel BPE도 use_regex: false, add_prefix_space: false 조합으로 받는다. 업로드한 파이프라인은 지원 프로필과 맞아야 하며, 임의의 normalizer나 WordPiece, Unigram은 일반적으로 지원하지 않는다. 파일을 받아들였다고 해서 모든 입력에 대해 정확히 같은 토큰화가 보장되는 것도 아니다.

실제 사용은 채팅 클라이언트에 얹는 형태다. Discord에서는 Vesktop/Vencord 테마로 적용한다. 컴파일한 TTF를 설치한 뒤 .theme.css 파일을 로컬 테마 폴더에 넣고, ThemeAttributes 플러그인을 켜서 특정 사용자 ID의 메시지에만 적용하는 식이다. ID를 비워두면 모든 메시지에 적용된다. Slack은 .user.js 유저스크립트를 브라우저 유저스크립트 매니저에 넣고 웹 Slack을 새로고침하는 방식이며, 표시 이름이나 멤버 ID로 대상을 지정한다. 두 경우 모두 코드 블록은 클라이언트 기본 모노스페이스 폰트를 유지한다.

이 도구가 겨냥하는 건 토큰 경계에 대한 감각이다. 같은 문장이라도 토크나이저에 따라 토큰 수가 달라지고, 그 차이가 비용과 컨텍스트 한도에 그대로 반영된다. 그런데 그 경계는 눈에 보이지 않는다. 폰트 단계에서 토큰을 같은 폭의 셀로 그려주면 문장이 어떻게 쪼개지는지 직관적으로 드러난다. 다만 만든 쪽은 이 결과물이 정확한 전체 메시지 토큰 카운터가 아니라고 선을 긋는다.

검증은 방향성 감사 형태로 이뤄졌다. 사용 가능한 폰트 바이너리 6종에 걸쳐 BPE 검사 17,904건과 ctok 케이스 2,355건을 로컬 토크나이저 참조와 비교했고, 브라우저 검사는 폭 60종과 줄바꿈 샘플 20건을 다뤘다. 특정 텍스트에 대한 표적 테스트일 뿐 전체 텍스트에 대한 보증은 아니라는 단서가 붙는다.

알려진 결함도 구체적으로 공개돼 있다. DeepSeek 쪽에서는 U+001C–U+001F 제어문자가 공백으로 잘못 분류된다. 공백 두 개 뒤에 U+001C가 오면 하나의 공백 토큰으로 합쳐지는 식이다. 2,984건 중 59건의 ID 불일치가 나왔고, 이 제어 분리자 버그는 아직 고쳐지지 않았다. GLM-5.3과 Llama 3의 실험적 프리셋은 폰트 전용 정규식 경계, 랭크드 BPE, 전체 조각 단축을 쓰는데 조각당 64바이트·32라운드 제한이 걸려 초과 시 [BPE limit]을 표시한다. 같은 감사에서 GLM 353건, Llama 354건이 이 한도에 걸렸다. Trinity Large Thinking과 Laguna M.1은 원본 전처리 파이프라인과 순서 있는 병합을 보존하는 방식으로 2,984건을 모두 통과했다.

Claude 경로는 성격이 다르다. ctok은 Claude 토크나이저의 검증된 구현이 아니라 비공식 근사다. 전체 ctok 어휘는 이제 들어가지만, 배포된 최소 비용 백엔드는 연결 요소당 256스텝 탐색 제한이 있어 넘치면 [limit]을 띄운다. 제로폭 공백, 워드 조이너, BOM, 소프트 하이픈 같은 보이지 않는 문자는 단어 경계를 억제하고 토큰을 하나 더 만들 수 있다. 버전별 785건 표적 케이스에서 v3는 개수·폭 29건과 의미만 45건, v4.7은 25건과 49건, v4.8은 16건과 8건 실패했다. 개수만 맞으면 정규화 오류를 숨길 수 있다는 지적도 붙는다.

구조적 한계는 폰트가 셰이핑 런을 넘어 토큰화할 수 없다는 점이다. 서식, 링크, 스크립트 전환, 양방향 텍스트, 폴백 폰트, 강제 줄바꿈이 런을 갈라놓으면 토큰 셀이 깨진다. 원본 폰트에 글리프가 없거나 폴백이 끼어들어도 같은 폭이 유지되지 않는다. 복잡 문자 셰이핑과 합자, 마크 위치 지정도 보편적으로 보존되지 않는다. Qwen의 NFC 지원은 분해 후 비시작 문자 8개 연속까지 처리하고 그 이상은 [NFC limit]을 표시한다. 브라우저와 네이티브 컴파일러의 유니코드 버전 차이도 새로 할당된 문자에서 드러날 수 있고, 렌더러가 폰트에 닿기 전에 텍스트를 정규화하면 토큰 경계 자체가 달라진다.

실무적으로 이 도구는 토큰 비용을 정밀 측정하는 용도보다, 프롬프트와 출력이 어떻게 쪼개지는지 감각을 잡는 용도에 가깝다. 토크나이저별 프리셋이 있다는 건 같은 문장도 모델마다 다르게 쪼개진다는 사실을 시각적으로 확인할 수 있다는 뜻이다. 다만 감사 결과가 보여주듯 공백 처리와 제어문자, 정규화 단계에서 오차가 남아 있고 브라우저 렌더링 환경에 따라 결과가 달라진다. 토큰 수를 정확히 세야 하는 작업이라면 별도의 토크나이저 라이브러리로 검증하는 편이 안전하다.