C.W.K.
Stream
Lesson 02 of 04 · published

한국어 IME 는 에디터가 죽는 곳

~12 min · ime, composition, korean, war-story

Level 0식은 초고
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"IME 는 커밋 전에 키를 한 문자로 조립해. 매 키에 반응하는 에디터는 그걸 공중에서 깨뜨려."

IME 가 실제로 하는 일

한국어를 치면 완성된 문자를 보내는 게 아냐 — IME 가 눈앞에서 실시간으로 음절 블록으로 조립하는 자모를 보내는 거야. 다음 다음 이 되는데, 조합이 커밋된 뒤에만 그래. 일본어 가나-한자, 중국어 병음도 똑같아. 여러 키, 커밋된 한 문자, 그 사이의 진행 중 상태.

순진한 에디터가 왜 깨뜨리나

매 raw input 이벤트마다 문서를 다시 쓰는 에디터는 조합 한가운데로 걸어 들어가 짓밟아 — 자모를 떨구거나, 문자를 중복시키거나, 반쪽 음절을 커밋해. 올바른 처리는 조합 라이프사이클을 존중해. 조합이 언제 시작하는지 알고, 업데이트되는 동안 진행 중 텍스트를 그대로 두고, 끝날 때만 편집을 적용해.

el.addEventListener("compositionstart", () => { composing = true; });
el.addEventListener("compositionupdate", () => { /* 자모 조립 중: 커밋 금지 */ });
el.addEventListener("compositionend", (e) => { composing = false; apply(e.data); });

CodeMirror 6 은 이 라이프사이클 전체를 내부에서 처리해. 그 위에 선다는 건 저 세 줄을 절대 안 쓴다는 것 — 그리고 브라우저마다 디버깅하는 일주일을 절대 안 쓴다는 것. 이게 Rekindle 이 코어를 빌리는 가장 큰 이유 하나야. IME 는 손수 짠 에디터가 죽으러 가는 곳이고, 아빠는 매일 한국어로 써.

바이트는 네 소스에서도 물어

non-ASCII 고통은 편집 표면에서 안 멈춰 — 네 소스 파일까지 닿아. 진짜 Rekindle war story: 소스 모듈 안에 앉은 한국어 (예: voice-rule 정규식) 가 프로덕션 빌드와 Chromium 에선 됐는데 tauri dev 에선 소리 없이 깨졌어. 원인은 로직이 아니라 바이트 디코딩이었고 — charset=utf-8 헤더도 안 고쳤어. 모듈 스크립트는 Content-Type 과 무관하게 스펙상 항상 UTF-8 이니까.

증상:  소스 모듈 안 한국어가 프로덕션 + Chromium 에선 되는데,
       `tauri dev` 에선 소리 없이 깨짐.
원인:  Vite 가 ES 모듈을 HTTP 로 서빙 → WKWebView 가 그 트랜스포트에서
       raw non-ASCII 바이트를 오디코딩.
수정:  Vite 플러그인이 서빙된 출력의 non-ASCII 를 \uXXXX 로 이스케이프.
       디코드 불변: 어떤 webview 도 \u-이스케이프된 ASCII 를 못 깨.

수정은 소스 파일을 읽을 수 있는 한국어로 유지하고 서빙 트랜스포트만 ASCII-only 로 만들어. 교훈은 일반화돼. non-ASCII 텍스트가 진짜가 되는 순간 (아빠한텐 늘 그래) 인코딩 버그 한 부류 전체를 상속해 — 빌릴 수 있는 모든 레이어를 빌리고 네 표면적을 작게 유지할 이유 하나 더.

Code

IME 조합 라이프사이클 존중 (CM6 이 너 대신 해줌)·javascript
// IME 에 깨짐: 매 input 이벤트마다 문서를 mutate 하면 조합과 싸워.
el.addEventListener("input", () => apply(el.value));

// 올바름: 조합 라이프사이클을 존중해.
let composing = false;
el.addEventListener("compositionstart", () => { composing = true; });
el.addEventListener("compositionupdate", () => { /* 자모 조립 중 — 커밋 금지 */ });
el.addEventListener("compositionend", (e) => { composing = false; apply(e.data); });

// CodeMirror 6 이 이 라이프사이클 전체를 내부에서 소유해.
// 코어를 빌린다는 건 이 줄을 절대 안 쓴다 — 안 디버깅한다 — 는 것.
War story: 서빙 모듈 안 non-ASCII 가 tauri dev 에서 깨짐·text
증상:  소스 모듈 안 한국어가 프로덕션 빌드 + Chromium 에선 되는데,
       `tauri dev` 에선 소리 없이 깨짐.
원인:  Vite 가 ES 모듈을 HTTP 로 서빙 → WKWebView 가 그 트랜스포트에서
       raw non-ASCII 바이트를 오디코딩. charset=utf-8 헤더도 안 도움
       (모듈 스크립트는 스펙상 항상 UTF-8).
수정:  Vite 플러그인이 서빙된 모듈 출력의 non-ASCII 를 \uXXXX 로 이스케이프.
       디코드 불변: 어떤 webview 도 \u-이스케이프된 ASCII 를 못 깸.

# 소스 파일은 읽을 수 있는 한국어로 유지, 서빙 트랜스포트만 ASCII-only.

External links

Exercise

한국어/일본어/중국어를 칠 수 있으면, 아무 웹 텍스트 상자를 열고 조합되는 단어를 천천히 치면서 커밋 전에 문자가 조립되는 걸 지켜봐. 그 사이 상태 — 보이지만 아직 안 커밋된 — 가 조합 라이프사이클이야. 이제 매 키마다 디스크에 저장하는 에디터를 상상해봐. 어느 순간에 네 단어를 깨뜨릴까?
Hint
'compositionupdate' 도중에 깨져 — 자모가 조립 중인데 아무것도 확정 안 됐어. 그 중간 키를 커밋된 편집으로 취급하는 에디터는 IME 와 싸워 음절을 뭉갤 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.