25년 넘게 Emacs를 써온 저자가, LLM을 ‘야크 깎기’ 대신 ‘야크 전기 양털깎이’처럼 활용해 elisp·문법·모드·로그 하이라이팅 등을 빠르게 구현한 경험과 주의점을 정리한다.
내 블로그와 프로젝트
저는 25년 넘게 Emacs를 사용해 왔고, 커스터마이징도 적당히는 다룰 줄 안다고 생각합니다. elisp도 쓸 수 있고, 수십 년 동안 쌓아온 각종 트윅의 찌꺼기(?)가 제 dotfiles 저장소에 고스란히 공개되어 있죠. advice도 작성해 봤고, 커스텀 함수를 키에 바인딩하기도 하며, 지금 이 글도 Emacs에서 작성 중입니다. 특히 Hugo 단축코드(shortcode) 안에서 fill-paragraph가 동작하는 방식이 짜증나서 급히 만들어 쓴 Hugo 마크다운용 커스텀 모드를 켠 상태로 말이죠.
Emacs가 우리 같은 사용자들에게 그렇게 매력적인 이유는 ‘무한한 커스터마이즈 가능성’ 때문입니다. 충분히 노력만 들이면 바꾸지 못할 것도, 가로채지 못할 것도, 다시 쓰지 못할 것도 거의 없고, 시간이 지나면 에디터가 마치 오래 신은 부츠처럼 내 몸에 딱 맞게 됩니다.
하지만 저는 사소한 불편함들을 그냥 참고 사는 경우도 많습니다. 내가 원하는 ‘정확히 그’ 패키지를 누가 만들어 둔 것도 아니고, 그걸 직접 만들 만큼 배워 들어가는 건 야크를 하나 더 깎는 일일 뿐이니까요. 그래서 손으로 하거나, 어떤 기능을 점프하는 단축키 대신 마우스를 쓰거나, 읽기 힘든 로그 출력에 그냥 적응하고 살거나, 보기 좋은 문법 하이라이팅을 포기하곤 합니다.
그런데 알고 보니 LLM은 elisp를 꽤 잘합니다. 오픈소스 학습 데이터도 엄청 많고 문서도 잘 갖춰져 있으니까요. 지금까지 저는 Gemini나 Claude를 써서 다음을 성공적으로 만들었습니다.
go-test-mode와 잘 어울리도록, 우리 애플리케이션의 Go 테스트 로그에 문법 하이라이팅을 적용하는 기능아쉽게도 가장 큰 성과들은 전부 업무용이었습니다. 회사에서 비용을 내는 AI를 회사 장비에서 사용해 만든 거라 여기 공개할 수는 없어요(언젠가 절차를 밟아 일부를 오픈소스로 공개할 수 있는지 알아볼 생각입니다). 대신, 제가 달성했던 예쁜 포맷팅이 어떤 느낌인지 보여주는(많이 가린) 로그 스크린샷을 첨부합니다.

그리고 그 결과를 만드는 코드도 약간 망가뜨린 버전으로 보여드리겠습니다. 주석과 문서의 대부분은 Gemini가 그대로 써 준 겁니다.
elisp(defgroup work-log-faces nil "Faces for Work integration test logs.") ;; Define faces (defface work-log-timestamp '((t :inherit font-lock-comment-face :foreground "#6c757d")) "Face for timestamps." :group 'work-log-faces) (defface work-log-level '((t :inherit font-lock-keyword-face :weight bold)) "Face for log levels (INFO, WARN)." :group 'work-log-faces) (defface work-log-message '((t :inherit font-lock-string-face :foreground "#e0e0e0")) "Face for the log message text." :group 'work-log-faces) (defface work-log-json '((t :inherit font-lock-constant-face :height 0.9)) "Face for the JSON payload." :group 'work-log-faces) ;; Define the matching rules (defvar work-log-font-lock-keywords '(("^\\([0-9T:.-]+\\)[ \t]+\\([A-Z]+\\)[ \t]+\\(.*?\\)[ \t]+\\({.*}\\)?$" (1 'work-log-timestamp t) (2 'work-log-level) (3 'work-log-message) (4 'work-log-json))) "Regex keywords to highlight structured logs.") ;; 1. Define the control flag (default to nil) (defvar -work-test-highlighting-active nil "If non-nil, apply custom log highlighting in go-test-mode.") ;; 2. Define the function that checks the flag (defun my:work-apply-logs-highlight () "Apply highlighting only if the Work test flag is active." (when -work-test-highlighting-active (font-lock-add-keywords nil work-log-font-lock-keywords t))) ;; 3. Add it to the hook ;; Note: go-test usually runs in `go-test-mode` or `compilation-mode` (add-hook 'go-test-mode-hook #'my:work-apply-logs-highlight)
지금까지 저는 LLM을 쓰는 주요 인터페이스로 두 가지를 사용했습니다. 하나는 Claude Code(특히 Emacs용 claude-code-ide)이고, 다른 하나는 Gemini 웹 인터페이스입니다. Claude는 Opus 4.5를, Gemini 3는 Thinking 또는 Pro를 사용했습니다. 참고로 대부분의 경우 회사가 토큰 비용을 내기 때문에, 저는 비용을 그렇게까지 따지면서 쓰진 않았습니다(미안해요, 비용 담당자 여러분). 모델이나 인터페이스에 대해 진지한 비교는 해 보지 않았는데… 요즘 모델들은 이 용도에서는 다들 꽤 잘하더라고요.
문법 하이라이팅과 백트레이스 작업 모두, 입력 샘플을 붙여넣고 “이렇게 해줬으면 좋겠다”라고 설명하는 것만으로도, 제 .emacs.d에 그대로 넣어 쓸 수 있는 elisp를 뽑아낼 수 있었습니다.
반면 ts 문법과 모드는 약간 더 왔다 갔다 해야 했고, 실제 디버깅도 필요했습니다(저도 했고 Gemini도 했죠). elisp 경험이 더 적었다면 ‘바이브 코딩(vibecode)’만으로 전체 솔루션을 끝까지 밀어붙이긴 어려웠을 거라고 생각합니다. 그래도 저는 그전까지 tree-sitter 문법이나(약간 손보는 정도가 아닌) major mode를 제대로 작성해 본 적은 없었는데, 필요한 것의 80%는 얻어낼 수 있었습니다.
Gemini가 써 준 font-lock 코드는 혁명적이진 않았습니다. 결국은 정규식 하나 쓰고, face 몇 개 정의하고, font-lock-add-keywords를 호출한 게 전부였죠. 하지만 저는 지금까지 font-lock이나 face 정의를 직접 만져본 적이 거의 없었고, 관련 함수를 머릿속에 외우고 있지도 않았습니다. 물론 시간을 들이면 알아낼 수는 있었겠지만, 제 시간 값을 생각하면 그럴 가치가 없었습니다. LLM이 야크의 대부분을 대신 깎아준 셈이죠.
그러니 다음번에 “이동(motion) 커맨드가 하나만 더 있었으면 좋겠는데”, “출력이 너무 못생겼는데”, “이 커맨드가 조금만 다르게 동작했으면 좋겠는데” 같은 짜증이 올라오면, Claude/Gemini/당신이 좋아하는 LLM에게 고쳐 달라고 해 보세요. 문제를 해결할 수 있을 뿐 아니라, 출력 결과를 잠깐만 검토해 보기만 해도(설마 LLM이 준 코드를 검토 없이 .emacs에 바로 넣을 건 아니죠? 진짜로요?), 몰랐던 서브시스템을 배우게 될지도 모릅니다. Neovim이나(조금 덜하긴 하지만) VSCode 같은, 확장 포인트가 그럴듯한 다른 에디터에서도 분명 비슷하게 할 수 있을 테니 그것들도 고쳐 보세요. 여름이 오면 야크들이 고마워할 겁니다.