두 번 오던 글자를 막았더니 가끔 안 왔다 — IceCmd 0.6.7
한글이 두 번 찍히던 걸 막은 장치가, 가끔 오는 한 번까지 버리고 있었다. 검사는 그걸 '통과'라고 적고 있었다. 빈 화면으로 시작하기와 드래그 복사, 그리고 검사 도구가 내 클립보드를 덮어쓴 이야기.
IceCmd 0.6.7을 냈다. 바뀐 것은 셋이다. 한글을 칠 때 가끔 글자가 사라지던 것을 고쳤고, 프로젝트를 빈 화면으로 열게 했고, 터미널에서 끌어 선택하면 바로 복사되게 했다. 그리고 이번에도 검사 도구가 사고를 쳤다. 그 얘기까지 적는다.
글자가 가끔 사라졌다
타이핑을 하다 보면 가끔 글자가 안 쳐지고 넘어갔다. 늘 그런 게 아니라 가끔이라 더 거슬렸다.
원인은 0.5.0에 넣은 고침에 있었다. 그때는 반대 문제를 잡았다. 한글이 두 번 찍혔다. 윈도우 한글 입력기가 음절을 넘겨줄 때 브라우저가 두 길로 신호를 보내고, 터미널 라이브러리(xterm.js)는 둘 다 받아 적었다. 한쪽은 키 입력(keypress)이고, 다른 쪽은 "입력창에 뭐가 새로 생겼나"를 한 박자 뒤에 확인하는 예비 경로다. 그 예비 경로가 입력기보다 늦게 돌면 같은 음절을 한 번 더 보냈다. 그래서 0.5.0은 예비 경로를 통째로 막았다.
그런데 그 예비 경로는 중복의 원인이기만 한 게 아니었다. 키 입력 신호 없이 글자만 들어오는 경우를 받아 주는 유일한 길이기도 했다. 그런 경우가 오면 이제 아무도 그 글자를 보내지 않는다. xterm.js 자신도 키가 눌린 동안 들어온 입력은 건너뛰게 되어 있다.
택배로 치면 이렇다. 같은 물건이 두 번 와서 두 번째 기사를 내보냈다. 그런데 첫 번째 기사가 가끔 안 오는 날이 있었고, 그날은 아무것도 오지 않았다.
이제는 그 경우만 따로 받아서 정확히 한 번 보낸다. 중복을 막는 장치는 그대로 두었다.
검사는 그걸 "통과"라고 적고 있었다
더 뼈아픈 건 이쪽이다. 0.5.0 때 이 고침을 지키려고 검사를 하나 넣었다. "입력기 키가 눌리고 입력창에 '가'가 생겨도, 아무것도 보내지 않으면 통과."
두 번 보내는 걸 막으려던 검사였다. 그런데 이 상황에는 첫 번째 전송도 없었다. 그러니까 이 검사는 글자를 삼키는 동작을 정상이라고 인증하고 있었다. 115개가 통과하는 사이에 그 하나가 버그를 지키고 있었다.
중복을 막는 검사는 "두 번째가 없다"가 아니라 "정확히 한 번"으로 써야 한다. 0번도 실패고 2번도 실패다. 여덟 가지 경우(키 입력이 있을 때, 없을 때, 키를 뗀 뒤, 조합 중, 조합 직후, 한 키에 두 글자 등)를 전부 "정확히 그 글자 한 번"으로 다시 썼다. 고치기 전 코드로 돌려서 새 검사가 실제로 실패하는 것("")까지 보고, 고친 뒤 통과하는 것("가")을 확인했다.
다만, 재현은 못 했다
솔직하게 적어 둔다. 디버그 기록을 켜고 cmd 창과 Claude 창에서 직접 100음절쯤 쳐 봤다. 한 글자도 사라지지 않았다. 그 시험에서는 입력기가 매번 정상 경로(조합 이벤트)로 글자를 넘겼고, "키 입력 신호 없이 글자만 오는" 경우는 한 번도 나오지 않았다.
그러니 이번 고침은 코드상 확인된 구멍을 막은 것이지, 눈앞에서 잡은 현상을 고친 것은 아니다. 그래도 계속 글자가 사라진다면 원인이 따로 있다는 뜻이고, 그때 쓸 기록 장치는 남겨 두었다.
빈 화면으로 시작한다
프로젝트를 추가하면 cmd 창이 하나 저절로 떴다. 앱을 다시 켜면 지난번 배치가 되살아났는데, 원래 claude였던 칸도 전부 cmd로 돌아왔다. CLI를 저절로 띄울 수는 없으니까. 결국 아무도 열지 않은 cmd가 화면을 차지했고, claude 버튼을 누르면 그 화면을 반으로 갈라야 했다.
이제 새 프로젝트도, 다시 켠 앱도 빈 화면으로 연다. 오른쪽에서 claude를 누르면 그 칸이 화면 전체를 쓴다. 페인 배치는 더 이상 저장하지도 않는다. 프로젝트 목록과 설정은 그대로 남는다.
끌면 복사된다
codex에서 글자를 끌어 선택하고 Ctrl+C를 누르면 codex가 종료됐다. 터미널 안에서 Ctrl+C는 프로그램의 것이다. 복사하려다 세션을 날리는 셈이었다.
이제 마우스로 끌어 선택하면 손을 떼는 순간 클립보드에 들어간다. Ctrl+C를 누를 일이 없다.
한 가지는 조심했다. claude처럼 마우스를 직접 다루는 프로그램은 자기 선택을 스스로 복사한다. 그 위에서 앱이 끼어들어 예전 선택을 복사해 버리면 claude가 복사한 것을 덮어쓰게 된다. 그래서 "선택이 바뀌었나"가 아니라 "이번 누름을 터미널이 선택으로 받았나"로 판단한다. 처음에는 "바뀌었나"로 만들었다가, 코드 리뷰에서 "같은 글자를 다시 끌면 복사가 안 된다"는 지적을 받았다. xterm.js는 직전과 같은 선택을 다시 알려 주지 않기 때문이다. 판단 기준을 바꾸고, 그 경우를 검사에 넣었다.
검사 도구가 내 클립보드를 덮어썼다
이번 사고는 여기서 났다. 마지막 검사를 돌리고 나서 보니 저장소에 처음 보는 빈 파일 다섯 개가 생겨 있었다. 이름이 cd, set, @if, del, echo였다. 전부 0.04초 안에 생겼다.
이름이 단서였다. 전부 검사 도구가 셸에 치는 명령어의 첫 단어다. 화면에 찍힌 D:\dev\IceCmd>cd 같은 줄이 통째로 다시 입력되면, cmd는 >cd를 "결과를 cd라는 파일로 보내라"로 읽는다. 그래서 빈 파일이 생긴다. 확인해 보니 내 클립보드에 검사 창의 화면 전체가 들어 있었다.
경위는 이렇다. 검사 도구는 창에 포커스가 없으면 클립보드 검사를 "건너뜀"으로 적는다. 그런데 판정만 건너뛰고 복사·붙여넣기 동작은 그대로 냈다. 웹뷰의 클립보드 기능은 포커스가 없을 때 실패하지 않는다. 기다린다. 그러다 창이 포커스를 받는 순간 밀린 일을 한꺼번에 했다. 화면 전체를 클립보드에 올리고, 곧바로 그것을 cmd에 붙여넣었다. cmd는 한 줄씩 실행했다.
다행히 줄마다 맨 앞의 경로만 실행돼 오류가 났고, 남은 건 빈 파일뿐이었다. 그래도 붙여넣은 줄 중에 실행 파일 경로가 있었다면 그 프로그램이 떴을 것이다.
이제는 포커스가 없으면 그 구간 동안 메모리 안의 가짜 클립보드로 바꿔 끼운다. 진짜 클립보드는 건드리지 않는다. 덕분에 그동안 "건너뜀"이던 클립보드 검사 셋이 실제로 판정되기 시작했다.
0.6.5 글에 이렇게 적었다. 검사가 실패하면 대상보다 먼저 검사 도구를 의심하라. 이번에는 하나를 더 배웠다. 검사가 건너뛰었다고 하면, 정말 아무것도 안 했는지 확인하라.
맥 사용자께
맥 지원은 0.6.6부터 들어갔지만, 이번 0.6.7은 윈도우에서 만들었다. 맥용 설치 파일(dmg)은 맥에서 서명과 공증을 거쳐야 해서 따로 올라간다. 그 전까지 맥에서는 0.6.6을 받으면 된다. 이번에 사이트 다운로드 카드를 고치면서 맥 내려받기도 처음으로 사이트에 올라갔다.
결과
검사 208개 통과, 1개 실패, 건너뜀 0으로 나갔다. 실패 하나는 "사이트 다운로드 카드가 실제로 나간 판보다 뒤처지지 않았나"를 보는 검사였다. 카드가 0.6.5에 머물러 있었기 때문이고, 이번 릴리스에서 카드를 0.6.7로 올렸다. 앱을 껐다 켜는 검사도 4개 모두 통과했다.
정리하면, 이번 판의 고침 셋은 모두 작다. 그런데 셋 다 "막으려다 다른 걸 같이 막은" 일이었다. 중복을 막다가 글자를 막았고, 아무 칸도 안 비게 하려다 화면을 막았고, 검사를 건너뛰려다 클립보드를 열어 두었다.
내려받기 → icenovel.com/service/icecmd