I built my husband a vim trainer with a Gemma coach that runs in the browser
브라우저에서 Gemma 코치가 동작하는 Vim 연습 앱을 만들었습니다
짧은 드릴로 Vim을 익히는 브라우저 앱 hjkl의 구현 과정을 소개합니다. CodeMirror의 실제 Vim 동작을 활용하고, Gemma 4 E2B를 WebGPU에서 실행해 힌트와 드릴 생성을 처리합니다.
- 주제
AI 요약
hjkl은 Vim을 배우려다 명령이 너무 많고 익숙한 편집기보다 느리다는 이유로 포기한 남편을 위해 만든 브라우저 앱입니다. 긴 튜토리얼 대신 작은 버퍼와 목표, 기준 타수(par)를 제시하는 짧은 드릴로 연습하게 합니다. 연습은 시뮬레이션이 아니라 실제 Vim 동작을 사용하며, 현재 17개 수업에 직접 작성한 드릴 124개가 들어 있습니다.
짧은 드릴과 복습 기능
수업마다 한 번에 몇 가지 명령만 소개하고, 화면에 치트 시트를 계속 표시합니다. 각 명령에는 VS Code에서 쓰던 단축키나 편집 습관을 함께 안내합니다. 예를 들어 dd는 Ctrl+Shift+K, o는 Ctrl+Enter에 대응한다고 보여줍니다. 대응하는 습관이 없으면 없다고 알려줍니다. <CR> 아래에 Enter, <Esc> 아래에 Escape처럼 낯선 키 표기도 익숙한 이름으로 풀어 씁니다.
드릴은 성공과 실패로 나누지 않습니다. 사용자는 언제나 목표를 완성하고, 기준 타수보다 많이 입력했다면 더 짧은 풀이를 확인합니다. 기준을 넘긴 드릴은 Review 목록에 기록되며, 목록은 효율이 낮았던 순서로 정렬됩니다. 기준 타수에 도달하면 목록에서 빠집니다. 앱에는 모든 명령을 모은 참조 페이지도 있어 키, 동작, 익숙한 VS Code 단축키로 검색할 수 있습니다.
실제 Vim 엔진과 검증
앱은 프레임워크 없이 Vite와 TypeScript로 만들었습니다. 편집기에는 CodeMirror 6과 @replit/codemirror-vim을 사용해 모션, 연산자, 텍스트 객체, 점 반복, 레지스터, 검색 등 실제 Vim 동작을 지원합니다.
모든 드릴에는 기준 풀이가 있습니다. 테스트는 풀이 키를 실제 Vim 엔진에 입력해 목표 상태에 도달하는지 확인합니다. 처음에는 기준 타수에 도달할 수 없는 드릴도 있었지만, 테스트가 이를 찾아냈습니다. 현재 테스트는 182개입니다. 검색 드릴에서는 Next를 누른 뒤 다음 검색이 이전보다 긴 버퍼의 위치로 이동하려다 오류가 발생하기도 했습니다. Vim 플러그인이 검색 기록과 점프 위치를 전역 상태로 보관했기 때문입니다. 앱은 드릴을 바꿀 때마다 Vim의 전역 상태를 초기화하도록 수정했습니다.
브라우저에서 실행하는 Gemma 코치
AI 코치는 선택 기능이며 기본값은 꺼져 있습니다. 켜면 Gemma 4 E2B를 한 번 내려받아 Transformers.js와 WebGPU를 이용해 Web Worker에서 실행합니다. 키 입력과 프롬프트는 브라우저 밖으로 나가지 않습니다. 텍스트 전용 경로로 모델을 불러 시각·음성 인코더를 제외하고, GPU가 FP16 셰이더를 지원하면 q4f16을 사용합니다. 지원하지 않으면 q4로 전환합니다. 더 가벼운 Gemma 3 1B도 선택할 수 있습니다.
작성자 환경에서 Gemma 4 E2B는 초당 약 6~7토큰을 생성해 한 문장 설명에 몇 초가 걸렸습니다. Gemma 3 1B는 초당 약 10토큰으로 빨랐지만, 같은 드릴을 시험했을 때 명령을 잘못 짚거나 피드백 대신 인사말을 내놓기도 했습니다. 그래서 기본 모델은 Gemma 4로 정했습니다. 코치는 기준 타수보다 많이 입력한 뒤 더 짧은 풀이와의 차이를 설명하고, 정답 전체를 공개하지 않는 힌트를 제공합니다.
처음에는 모델에 입력한 키를 그대로 전달했습니다. x를 일곱 번 입력한 사례에서 모델이 dd를 썼다고 잘못 설명했습니다. 앱은 모델이 Vim 키 입력을 직접 해석하게 두지 않고, 키를 Vim이 읽는 단위로 묶어 동작 설명을 붙입니다. xxxxxxx 대신 “x ×7 (문자 삭제)”를 전달하고, cwblue<Esc> 대신 “cw (단어 끝까지 변경), ‘blue’ 입력, <Esc> (일반 모드로 복귀)”라고 구성합니다. 수정 뒤 테스트 사례에서 설명이 정확하게 나왔습니다.
생성 드릴은 앱이 풀이를 검증합니다
Gemma가 텍스트, 커서 위치, 풀이 키까지 드릴 전체를 만들게 한 첫 시도에서는 초안 12개 중 2개만 검증을 통과했습니다. 커서 표시가 빠지거나 안내와 맞지 않는 키가 생성되고, 텍스트 단어가 붙는 문제가 있었습니다. 작성자는 생성 역할을 나눴습니다. Gemma는 SQL 쿼리나 장보기 목록, CSS 규칙 같은 짧은 텍스트와 지시문만 작성합니다. 앱은 해당 수업의 직접 작성 드릴에서 키 패턴을 가져와 새 텍스트에 적용한 뒤 실제 Vim으로 실행해 목표와 기준 타수를 구합니다.
지시문도 확인합니다. Gemma가 실제 변경 내용을 설명하지 않으면 “milk를 eggs로 바꾸기”처럼 단순한 문장으로 대체합니다. 이 방식으로 생성 묶음마다 5개 중 5개가 검증을 통과하며, 생성에는 약 15초가 걸립니다. 앱이 드릴을 화면에 보여주기 전에 직접 실행하므로 생성된 드릴은 모두 기준 타수로 풀 수 있습니다.
배포와 로컬 실행의 이유
앱은 Cloudflare Workers 정적 에셋으로 배포합니다. ONNX Runtime의 wasm 파일은 약 27MB로 Cloudflare의 파일당 25MiB 제한을 넘습니다. Transformers.js가 정확한 버전으로 고정한 파일을 jsDelivr에서 불러오므로 빌드 결과에서는 제외했습니다.
Gemma를 브라우저에서 실행하면 사용량에 따른 API 비용이나 서버, API 키를 관리하지 않아도 됩니다. 계정 없이 사용할 수 있고 키 입력과 실수도 사용자 기기에 남습니다. 작성자는 노트북 GPU에 맞는 모델 크기를 고르고 두 Gemma 모델을 같은 드릴에서 비교했으며, 한 번의 클릭으로 모델을 바꾸도록 만들었습니다. 앱은 hjkl.pinkpixel.dev에서 제공하며, 물리 키보드가 있는 데스크톱 브라우저와 WebGPU가 필요합니다.
dev.to 반응
- @danielecangi — 정말 마음에 듭니다. 정말 보기 좋게 만들었네요! 다만 Vim과 저는 한 번도 마음이 맞은 적이 없습니다 🤪
- @karan_raj_1899d579c759c1f — 정말 대단합니다. 남편분뿐 아니라 저 같은 친구들에게도 유용하겠네요. Vim을 연습할 좋은 방법입니다.
원문: dev.to / 번역·요약: Trawling