Ghostty 개발 로그 004
원문은 Mitchell Hashimoto님이 에 게재했습니다. 이 블로그 구독하기
안녕하세요! Ghostty의 네 번째 공식 개발 로그에 오신 것을 환영합니다 👻!
이전 개발 로그를 놓치셨거나 Ghostty가 무엇인지 더 알고 싶으시다면, 이 웹사이트의 Ghostty 페이지를 참고해 주세요.
커뮤니티 소식
지난주에 공개 Discord 서버를 공유했고 Ghostty와 Zig에 대한 발표도 했습니다. 놓치셨다면 꼭 확인해 보세요. 새로운 베타 초대는 Discord 커뮤니티를 통해 배포하고 있습니다. 그리고 Discord 커뮤니티가 600명을 넘어섰습니다. 와우!
지난 Ghostty 개발 로그 이후 클로즈드 베타 인원이 30명 정도에서 100명 정도로 늘었습니다. 프로젝트 초기에는 베타를 새로 모집할 때마다 소수만 추가했지만, Ghostty가 점점 안정화되면서 이제는 한 번에 15~20명씩 무리 없이 늘릴 수 있게 되었습니다. 아주 긍정적인 신호라고 생각합니다!
멋진 GUI
터미널 내부 구조와 오래된 기술이라는 깊은 영역으로 들어가기 전에, GUI 개선 사항을 이야기하고 눈이 즐거운 것들도 살펴보겠습니다. 깊이 있는 기술적 내용을 찾고 계신다면, 뒤쪽 섹션에서 본격적으로 다룹니다.
macOS와 Linux 모두에서 네이티브 GUI가 크게 발전했습니다. Linux에서는 Ghostty에 헤더바 메뉴, 넓은 탭(설정 가능), 정보 창 등이 추가되었습니다. Gnome Terminal이나 다른 GTK 기반 터미널과 나란히 놓고 보면 Ghostty가 자연스럽게 어우러집니다:

Linux에서는 Sway, Hyprland 등 다양한 컴포지터를 통해 Wayland를 사용하는 베타 테스터가 여러 명 생겼습니다. 여러 문제를 수정했고 이제 Ghostty는 Wayland에서 훌륭하게 동작합니다.
macOS에서는 window-theme 설정을 추가해 시스템 설정을 따르는 대신 Ghostty를 항상 다크 모드나 라이트 모드로 고정할 수 있게 했습니다. 저는 개인적으로 macOS는 라이트 모드로 쓰지만 터미널은 항상 다크로 유지하고 싶어서 다크로 강제해 두고 있습니다. 또 포커스를 잃은 분할 화면은 흐리게 표시되도록 했는데(이 역시 설정할 수 있습니다), 정말 아름답게 보입니다:

Ghostty가 내세운 목표 중 하나는 현재 사용 중인 플랫폼을 위해 처음부터 만들어진 것처럼 느껴지는 네이티브 경험을 제공하는 것입니다. 저희는 GUI 개선을 매우 중요하게 여기며, Ghostty가 실행되는 플랫폼의 관용적인 디자인을 따르도록 하고 있습니다. 위에서 소개한 개선 사항들은 이러한 이상을 향한 저희의 진전을 보여줍니다.
Vim 버그를 찾았습니다! 그리고 다른 말썽꾸러기 프로그램들.
Ghostty는 대부분의 기간 동안 자신을 TERM=xterm-ghostty로 알려왔습니다. xterm- 접두사를 붙인 이유는 꽤 많은 말썽꾸러기 프로그램들이 TERM 값을 문자열 매칭으로 검사해 터미널이 특정 기능을 지원하는지 판단하기 때문입니다. 이는 잘못된 방식이며 아주 버릇없는 행동이니, 이렇게 하지 마세요! 올바른 방법은 terminfo 데이터베이스를 조회하는 것입니다.
Terminfo 자체만으로도 매우 흥미로운(주로 너무나도 저주받았기 때문에) 주제입니다. 언젠가 terminfo에 대해 따로 블로그 글을 쓰고 싶습니다. 이번 개발 로그에서는 terminfo를 자세히 다루지 않겠습니다. 이 오래된 괴물의 모습을 살짝 보고 싶으시다면 터미널에서 infocmp를 실행해 보세요.
지난 한 달 동안 저희는 xterm- 접두사를 떼고 단순히 TERM=ghostty가 되려고 노력해 왔습니다. 그 과정에서 저희 자체 terminfo 데이터베이스의 버그뿐 아니라 업스트림 문제도 많이 발견했고, 발견하는 대로 수정해 왔습니다.
가장 최근에는 vim 버그 때문에 진행이 막혔습니다. Vim 9.0은 Kitty Keyboard Protocol을 지원하지만, 이를 지원하는 터미널 목록을 하드코딩하고 terminfo 데이터베이스를 제대로 존중하지 않습니다. 이는 버그이며, 놀랍게도 Ghostty 커뮤니티 멤버 한 분이 이를 수정하는 풀 리퀘스트를 열어주셨습니다. 이 문제는 Neovim에는 영향을 주지 않습니다.
Vim은 뭐, 조금 중요한 프로그램이니까1, 해당 버그가 수정되고 하위 배포판들이 널리 업데이트될 때까지는 아쉽게도 다시 TERM=xterm-ghostty로 되돌릴 수밖에 없었습니다. 그럼에도 지금까지 이룬 진전이 자랑스럽고, 곧 xterm의 족쇄를 벗어던질 수 있을 것이라 낙관하고 있습니다.
다른 업스트림 버그들도 발견했는데, Ghostty 커뮤니티가 문제를 제보하는 데 그치지 않고 수정까지 제공해 주어 자랑스럽습니다. Tim은 특히 심각한 문제를 수정하기 위해 인기 있는 Go 라이브러리 tcell에 PR을 보냈습니다. 이 라이브러리는 lazygit, lazydocker 등 인기 있는 TUI 애플리케이션들이 사용하는 중요한 라이브러리입니다. 이 수정이 없으면 TERM=xterm-256color로 명시적으로 실행하지 않는 한 해당 프로그램들이 모두 깨진 문자를 쏟아냅니다. 곧 반영되기를 바랍니다!
이러한 버그나 수정 사항 중 어느 것도 Ghostty에만 국한된 것이 아니며, 모두 낯선 터미널 에뮬레이터 앞에서도 프로그램이 더 견고하게 동작하도록 만드는 데 기여할 것입니다.
이 작업은 주로 Tim Culverhouse가 주도했으며, 그가 해준 모든 것에 깊이 감사하고 있습니다.
XTGETTCAP (#563)
terminfo의 큰 문제 중 하나는 실행 중인 프로그램이 직접 확인해야 하는 로컬 파일에 의존한다는 점입니다. 즉, 원격 호스트에 SSH로 접속했는데 해당 호스트에 내 터미널의 terminfo가 없으면, 프로그램은 내 터미널이 무엇을 할 수 있는지 판단하지 못해 잘못 동작하거나 최적으로 동작하지 못할 수 있습니다.
XTGETTCAP은 로컬 파일을 읽는 대신 VT 이스케이프 시퀀스를 통해 터미널의 terminfo 데이터베이스를 조회할 수 있게 하는 터미널 기능입니다. 여기에는 몇 가지 이점이 있습니다. 프로그램이 바이너리 형식을 파싱하거나 파일 경로를 찾을 방법을 알 필요가 없고, 원격에서 실행 중이더라도 terminfo를 조회할 수 있습니다.
XTGETTCAP은 이렇게 생겼습니다:
# Query for stylized underline support (key "Smulx")
ESC P + q 536D756C78 ESC \
# Response from the terminal (Smulx=\E[4:%p1%dm)
ESC P 1 + r 536D756C78=5C45343A25703125646D ESC \완전히 자명하죠, 그렇죠? 사실 그렇게 나쁘지 않습니다. 프로그램은 ESC P + q <key> ESC \ 형태의 텍스트를 보내는데, 여기서 <key>는 16진수로 인코딩된 문자열입니다. 위 예시에서는 "Smulx"를 16진수로 인코딩했습니다. 그리고 응답에는 해당 terminfo 항목의 키와 값이 역시 16진수로 인코딩되어 들어 있습니다.
저희 구현에서 특히 멋진 점은 Zig의 comptime 기능을 활용해 모든 가능한 요청과 응답을 컴파일 타임에 생성했다는 것입니다. 이는 실제 terminfo 파일을 생성하는 것과 동일한 소스 파일에서 만들어지므로, 저희 terminfo와 XTGETTCAP은 항상 완벽하게 동기화된 상태를 유지합니다.
XTGETTCAP 성능은 사실 전혀 중요하지 않지만, 저희 XTGETTCAP 구현은 컴파일 타임에 최적화된 룩업 테이블 덕분에 말도 안 되게 빠릅니다. 할당이나 문자열 포맷팅 등을 할 필요가 없습니다. 단순히 룩업하고, 안정적인 문자 포인터를 찾아 pty에 바로 씁니다. 정말 멋지죠.
이를 생성하는 Zig 스니펫을 살펴보세요. Zig comptime이 정말 좋습니다.
가변 폰트 (#345)
대부분의 폰트는 "Bold", "Heavy", "Medium", "Italic" 등과 같은 고정된 스타일 세트를 제공합니다. 반면 가변 폰트는 weight, slant 등 다양한 축을 일정 범위 내의 연속적인 값으로 조절할 수 있는 단일 폰트 페이스를 제공합니다. Ghostty는 이제 가변 폰트와 variation 축 설정을 지원합니다.
먼저 가변 폰트가 어떻게 보이는지 살펴보겠습니다. Inconsolata에는 가변 폰트 버전이 있으며, 아래 영상에서 문자 너비와 두께를 미세하게 조절하는 모습을 볼 수 있습니다.
Inconsolata를 사용했을 때 Ghostty의 기본 모습입니다. 폰트 variation 축을 전혀 설정하지 않은 상태입니다:

그리고 weight를 살짝 수정한 설정입니다. 차이는 미묘하지만 확인하실 수 있습니다. 먼저 설정 내용이고, 그 다음이 스크린샷입니다.
font-family = Inconsolata
font-variation-bold = wght=500

위에서 왜 weight가 아니라 wght일까요? variation의 키는 폰트에 의해 결정되며, 폰트 variation은 반드시 네 글자여야 합니다. 이는 표준화되어 있지 않기 때문에 weight를 wght로 안전하게 변환할 수 없습니다. Ghostty 설정에서는 리터럴한 variation 키를 그대로 사용하며, 사용자는 FontDrop과 같은 폰트 검사 프로그램을 이용해 유효한 키를 확인할 수 있습니다.
가변 폰트 지원은 터미널 에뮬레이터 중에서는 비교적 드물기 때문에, 이를 지원하게 되어(게다가 크로스 플랫폼으로!) 자랑스럽습니다. 이런 기능들을 통해 터미널을 원하는 대로 세밀하게 조정하고 딱 맞는 느낌으로 만들 수 있습니다.
마무리
가장 최근 베타 테스터 분들께 다시 한번 감사드립니다. 매번 새로운 분들이 합류할 때마다 특정 분야에 깊은 지식을 가지고 있거나 터미널을 새롭고 독특한 방식으로 사용해 문제를 찾아내 주는 정말 놀라운 분들을 만나게 됩니다.
최신 소식을 받아보고 싶으시다면 Twitter나 Mastodon에서 팔로우해 주세요(링크는 푸터에 있습니다). 이 블로그는 RSS 피드도 제공합니다.
부우. 👻
각주
삼가 고인의 명복을 빕니다 Bram Moolenaar, 그리고 우리에게 주신 모든 것에 감사드립니다. ↩
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기