TL;DR: 터미널 커서 스타일은 여러 소프트웨어 계층이 순차적으로 덮어쓰며, 최종 모양은 가장 하위 계층(현재 실행 중인 애플리케이션)이 결정한다.
터미널 커서의 시각적 모양(블록, 빔, 언더라인)은 단일 설정값이 아니라 터미널 에뮬레이터, 쉘, 텍스트 편집기 등이 각자 제어할 수 있는 상태이다. 각 계층은 자신의 컨텍스트에 맞춰 커서 스타일을 변경하며, 하위 계층의 변경 요청이 상위 계층의 정적 설정을 실시간으로 덮어쓴다.
계층은 일반적으로 다음과 같다.
- 터미널 에뮬레이터 기본 설정: WezTerm의
config.default_cursor_style이나 iTerm2, Alacritty의 유사 설정. 창이 처음 열릴 때 적용되는 초기값 역할을 한다. - 쉘 및 쉘 플러그인: Zsh의
zsh-vi-mode와 같은 플러그인은 편집 모드(Normal/Insert)에 따라 DECSCUSR 시퀀스를 전송해 커서를 바꾼다. 이 변경은 터미널이 현재 표시하는 커서를 즉시 덮어쓴다. - 텍스트 편집기 내부 설정: Neovim/Vim은
guicursor옵션을 통해 자체 편집 모드에 따른 커서 모양을 관리하며, 쉘의 커서 상태를 완전히 대체한다.
충돌이 발생하는 근본 원인은 각 계층이 자신이 '현재 활성'이라고 판단하는 시점에 커서를 변경하려 하기 때문이다. 예를 들어 WezTerm을 SteadyUnderline으로 설정했더라도, zsh-vi-mode가 Normal 모드에서 블록 커서로 강제하면 그 모양이 보인다. Neovim 안에서는 guicursor 설정이 모든 것을 제어한다.
디버깅은 이 계층을 의식적으로 분리하면서 진행한다. WezTerm 설정이 안 먹힌다면, 먼저 쉘 플러그인의 영향을 제거해본다(zsh-vi-mode에서 ZVM_CURSOR_STYLE_ENABLED=false). Neovim 내부에서 문제가 보인다면 guicursor 설정을 확인한다. 계층별로 책임 소재를 좁혀가면, 설정이 '무시되는' 것이 아니라 '덮어쓰여지는' 정상 동작임을 알 수 있다.
이 모델을 이해하면 터미널 관련 시각적 설정이 예상대로 적용되지 않을 때, 단순히 설정 파일 문법 오류를 넘어서 각 소프트웨어 계층의 상호작용을 점검할 수 있다. 특히 터미널 에뮬레이터 설정은 최종적이지 않으며, 동적으로 상태를 바꾸는 프로그램(모드 있는 쉘, 편집기)이 있다면 그 프로그램의 설정이 우선한다는 점이 핵심이다.
관련 항목
- DECSCUSR 시퀀스 (터미널 커서 제어 표준)
- zsh-vi-mode 플러그인 동작
- Neovim guicursor 옵션
- WezTerm 설정 로드 순서
Open questions
- 터미널 멀티플렉서(tmux, screen)가 이 계층 구조에 어떤 영향을 미치는가?
- 어떤 터미널 에뮬레이터는 애플리케이션의 커서 변경 요청을 완전히 무시하는 옵션을 제공하는가(예: WezTerm 이슈 #2781)?