libghostty가 온다
2년여 전, Ghostty에 대한 첫 공개 발표 중 하나에서 저는 libghostty에 대한 비전을 공유한 적이 있습니다. 어떤 애플리케이션이든 완전한 기능을 갖춘 현대적이고 빠른 터미널 에뮬레이터를 직접 내장할 수 있게 해주는 임베디드 라이브러리라는 구상이었죠. 이제 libghostty가 드디어 모습을 갖추기 시작했고, 앞으로의 계획을 더 자세히 공유하게 되어 기쁩니다.
첫 번째 libghostty 라이브러리는 libghostty-vt가 될 것입니다. 터미널 시퀀스를 파싱하고 터미널 상태를 유지하는 API를 제공하는 제로 의존성 라이브러리로, 실제 환경에서 검증된 Ghostty의 핵심에서 직접 추출한 것입니다. 심지어 libc도 필요하지 않습니다!
알림: 이 글은 주로 libghostty의 로드맵을 업데이트하고 처음으로 출시될 컴포넌트를 소개하기 위한 글입니다. Zig API는 현재 테스트용으로 사용할 수 있지만, C API는 아직 준비되지 않았으며 곧 공개될 예정입니다. 두 경우 모두 아직 초기 테스트 단계이며 일반적인 사용을 위한 수준은 아닙니다.
왜 libghostty인가?
먼저 제가 왜 libghostty가 반드시 필요하다고 생각하는지 배경부터 이야기해보겠습니다.
어떤 형태로든 터미널 에뮬레이션을 구현한 프로그램은 수백 가지에 이릅니다. 가장 대표적인 것은 Ghostty, Kitty, iTerm2 같은 범용 터미널 에뮬레이터입니다. 하지만 tmux나 zellij 같은 터미널 멀티플렉서 역시 완전한 터미널 에뮬레이터입니다!1 에디터들도 자체 터미널 에뮬레이터를 내장하고 있습니다. JetBrains 제품군의 jediterm, VS Code의 Xterm.js, Zed에서 사용하는 Alacritty 등이 그 예입니다.
완전히 동작하는 터미널 에뮬레이터 외에도, 많은 웹사이트와 애플리케이션이 로그나 명령어 출력을 표시하기 위해 읽기 전용 터미널 에뮬레이션을 구현합니다. 예를 들어 GitHub Actions 출력은 간단한 색상 시퀀스를 파싱합니다(그 이상은 거의 처리하지 않습니다). Vercel이나 Render 같은 호스팅 제공업체도 빌드 로그 내에서 줄을 지우고 다시 그리거나 색상을 파싱하는 등 간단한 형태의 터미널 에뮬레이션을 구현하고 있습니다.
이러한 구현 중 상당수는 그때그때 만든 일회성 해결책입니다. 공유 라이브러리나 코드베이스를 사용하지 않습니다.2 터미널 에뮬레이션은 겉보기에 단순해 보이지만 예상치 못한 복잡성과 엣지 케이스가 가득한 고전적인 문제입니다.3 그 결과 대부분의 구현은 불완전하고, 버그가 많으며, 느립니다.4
정확성 문제를 떠나, 어떤 형태든 터미널 에뮬레이션을 구현하는 것 자체가 대부분의 개발자에게는 시간 낭비입니다. 터미널 에뮬레이션은 JetBrains, Visual Studio Code, GitHub, Vercel, Render 등의 핵심 비즈니스가 아닙니다. 어디서나 일관되게 동작하는 안정적이고 재사용 가능한 솔루션이 있다면 그들에게도 분명 도움이 될 것입니다.
이에 대한 저의 답이 바로 libghostty입니다. 크로스 플랫폼에 의존성을 최소화하고 C API를 노출하여, 어떤 애플리케이션이라도 어디서나 기능이 풍부하고 정확하며 빠른 터미널 기능을 내장할 수 있게 하는 라이브러리입니다.
시작: libghostty-vt
첫 번째 libghostty 라이브러리인 libghostty-vt는 터미널 시퀀스를 파싱하고 커서 위치, 현재 스타일, 텍스트 줄 바꿈 등 터미널 상태를 유지하는 API를 제공하는 제로 의존성(libc조차 필요 없는) 라이브러리입니다.
터미널 시퀀스 파싱은 터미널 에뮬레이터의 가장 핵심적인 기능으로, Ghostty 같은 완전한 터미널 에뮬레이터부터 GitHub Actions나 Vercel 빌드 출력 같은 단순한 읽기 전용 스타일 뷰까지 모두 필요합니다.
상태 다이어그램은 처음 보면 비교적 단순해 보이지만, 제대로 구현하는 것은 예상보다 훨씬 어렵습니다. 예를 들어 jediterm은 intermediate를 올바르게 처리하지 못해 이 글 작성 시점 기준으로 모든 JetBrains 에디터에서 널리 지원되는 “커서 모양 변경” 시퀀스가 문자 하나를 삼켜버리는 문제가 있었습니다.
스타일만 파싱하는 경우, 많은 개발자가 전체 상태 다이어그램을 건너뜁니다. 대신 웹에서 간단히 검색해 \e[31이나 \e[41m 같은 단순한 ANSI 시퀀스만 파싱한 뒤 “색상 지원”이라고 주장합니다. 하지만 스타일 관련 시퀀스는 그보다 훨씬 복잡합니다. 예를 들어 RGB를 지원하는데, RGB 자체도 열 가지가 넘는 형식으로 표현될 수 있습니다. 그리고 저는 이 복잡한 스타일 시퀀스를 올바르게 렌더링하는 웹 콘솔을 아직 하나도 찾지 못했습니다.5
libghostty-vt는 이 모든 문제를 해결하고자 합니다.
libghostty-vt는 Ghostty에서 추출된 것으로, SIMD로 최적화된 파싱, 매우 우수한 유니코드 지원, 고도로 최적화된 메모리 사용량, 퍼징과 Valgrind 테스트를 거친 견고한 코드베이스, Kitty Graphics Protocol이나 Tmux Control Mode 파싱 같은 뛰어난 기능 호환성 등 실제 환경에서 검증된 모든 이점을 그대로 계승합니다.
이 모든 기능이 단일 제로 의존성 C API(libc에도 의존하지 않습니다)로 패키징되어, 어떤 인기 언어 생태계에도 쉽게 임베드할 수 있습니다.
풋프린트가 매우 작기 때문에 libghostty-vt는 뛰어난 이식성을 갖습니다. 초기에는 Ghostty 애플리케이션의 주요 타깃인 macOS와 Linux의 x86_64, aarch64 아키텍처를 지원할 예정입니다. 하지만 Windows, 임베디드 기기, WASM을 통한 웹 등 더 다양한 타깃으로 지원을 확대할 계획입니다. 범위가 더 좁은 만큼 libghostty는 GUI인 Ghostty보다 더 폭넓은 플랫폼을 지원하게 될 것입니다.
장기 계획
libghostty-vt는 시작에 불과합니다. 장기적으로는 입력 처리(키보드 인코딩이 대표적입니다), GPU 렌더링(OpenGL이나 Metal 서피스를 넘겨주시면 나머지는 저희가 처리합니다), 전체 터미널 뷰를 담당하는 GTK 위젯과 Swift 프레임워크 등 추가 기능을 제공하는 더 많은 libghostty-<x> 라이브러리를 제공할 예정입니다.
기반이 되는 핵심 요소들이 안정화되면, 점점 더 많은 기능을 계속해서 제공할 것입니다. 이러한 라이브러리들은 의존성 요구사항, 코드 크기, 전반적인 유지보수 복잡성을 최소화하기 위해 하나의 라이브러리 패밀리 형태로 구성될 예정입니다.
libghostty-vt 현황
방금 libghostty-vt를 Zig 모듈로 노출하는 풀 리퀘스트를 머지했습니다. 이 PR에는 간단한 예제 프로그램도 포함되어 있습니다. Zig 개발자라면 지금 바로 libghostty-vt를 시험해 볼 수 있습니다.
C API는 아직 준비되지 않았지만, 현재 제가 바로 작업하고 있는 부분이며 곧 테스트용으로 공개될 예정입니다. 필요한 작업은 C API를 정의하는 것뿐입니다. 핵심 로직은 당연히 이미 모두 존재하며 Ghostty에서 수년간 사용되어 왔습니다. 게다가 Ghostty macOS 앱은 이미 내부 전용 C API를 사용하고 있습니다.
내부 전용 C 헤더를 보셨다면, 그 복잡함은 무시해 주시기 바랍니다. 좋은 C API가 아닙니다. 내부 전용으로 macOS 애플리케이션의 요구사항을 충족하기 위해 존재할 뿐입니다. 이것은 일반적으로 사용할 수 있는 libghostty가 아니지만, 이미 실제 상용 제품에서 Ghostty를 임베드하는 데 사용되고 있습니다. 더 넓은 사용을 위한 C API는 처음부터 새로 정의할 예정입니다.
저는 libghostty를 Ghostty 애플리케이션과 별도로 버저닝할 계획입니다. 이 블로그 글은 공개 알파(API 안정성을 보장하지 않습니다)를 알리는 글이며, C API가 준비되면 일부 개발자들이 직접 사용해 보고 언어 바인딩을 작성해 주기를 기대합니다.
향후 6개월 안에 libghostty-vt의 태그 버전을 출시하는 것이 목표지만, 준비 상태에 따라 달라질 것입니다.
피드백을 기다립니다
지금은 libghostty의 API를 설계하는 중요한 단계에 있으며, API를 잘 설계하는 가장 좋은 방법은 실제 사용자의 피드백을 듣는 것입니다. Ghostty 자체가 하나의 사용자이고, 다른 libghostty 기반 프로젝트를 진행 중인 커뮤니티 멤버들도 있지만, 가능한 한 많은 분들의 참여가 필요합니다!
여러분의 프로젝트나 조직에서 libghostty를 활용할 만한 사례가 있다면 Ghostty Discord에 참여해 이 작업을 진행 중인 개발자들과 협업해 주시기 바랍니다. Discord 참여를 원하지 않으시면 이메일로 연락해 주세요(이 웹사이트 하단에 이메일 주소가 있습니다).
“알파”라는 표현은 API(함수와 타입) 자체에 대한 것입니다. 핵심 로직은 Ghostty와 공유되며 실제 환경에서 매우 안정적이고 검증되었습니다.
다음 단계
Ghostty 애플리케이션이 마침내 안정화되어 libghostty라는 목표를 향해 나아갈 수 있게 되어 매우 기쁩니다. libghostty는 Ghostty의 다음 개척지이며, 독립적인 애플리케이션으로서의 Ghostty보다 훨씬 더 큰 영향력을 만들어낼 수 있다고 생각합니다.
걱정하지 마세요, Ghostty 애플리케이션에 대한 변화도 충분히 많이 준비되어 있으며, 그렇다고 해서 그에 대한 제 열정이나 계획이 줄어드는 것은 아닙니다. libghostty가 더 널리 사용될수록 Ghostty 자체가 libghostty를 사용하는 만큼 더 기능이 풍부하고 안정적인 Ghostty 애플리케이션이 될 것입니다.
Boo. 👻
각주
이 프로그램들은 자식 프로세스의 pty를 소유하고, 모든 이스케이프 코드를 파싱하며, 화면 상태를 관리하고, 궁극적으로는 부모 터미널 에뮬레이터에 자체 이스케이프 코드를 내보내 렌더링합니다. ↩
많은 웹사이트가 Xterm.js를 사용하고 GTK 생태계에는 libvte가 있습니다. 하지만 이는 전체 중 아주 일부에 불과하며, 앞서 언급한 예시들도 각각 나름의 한계를 가지고 있습니다. ↩
저는 3년 가까이 터미널 에뮬레이션 작업을 해왔지만, 아직도 이상한 엣지 케이스를 계속 발견하고 있습니다. 여전히 이상한 엣지 케이스를 발견하고 있습니다. ↩
실제로 영향이 컸던 이슈 몇 가지를 소개합니다: jediterm은 intermediate를 제대로 처리하지 못했고, Apple의 Terminal.app은 DCS 시퀀스를 그대로 출력에 흘려보냅니다. 특정 제품을 비난하려는 것이 아니라, 이런 문제들이 존재하며 터미널 에뮬레이션을 제대로 구현하는 것이 얼마나 어려운지를 보여주기 위함입니다. ↩
많은 범용 터미널 에뮬레이터들도 이를 잘못 처리했는데, 9개월 전까지만 해도 Ghostty 역시 마찬가지였습니다. ↩
글을 무작위로 읽기