wterm: Vercel에서 선보이는 Ghostty 기반 웹 터미널

BBetter Stack
컴퓨터/소프트웨어AI/미래기술

Transcript

00:00:00이것은 Vasell이 만든 웹 기반 터미널 에뮬레이터인 Wterm입니다.
00:00:03캔버스가 아닌 DOM에 직접 렌더링되죠.
00:00:06그래서 텍스트 선택, 브라우저 찾기,
00:00:08그리고 스크린 리더까지 모두 문제없이 작동합니다.
00:00:10Zig로 작성되어 12KB WASM 바이너리로 컴파일되며,
00:00:14LibGhosty로 구동되는 옵션 백엔드도 있어서,
00:00:17고스트 터미널(Ghosty terminal)을 구동하는 동일한 엔진으로
00:00:19브라우저에서 완벽한 터미널 호환성을 제공합니다.
00:00:22하지만 12KB짜리 WASM 바이너리가
00:00:24정말로 네이티브 터미널 에뮬레이터를 대체할 수 있을까요?
00:00:28아마 아닐 겁니다. 하지만 구독하고 같이 확인해 보죠.
00:00:33웹 터미널은 이제 거의 어디에나 있습니다.
00:00:36GitHub Codespaces 같은 클라우드 IDE,
00:00:39Portainer나 Qualify 같은 인프라 도구,
00:00:41심지어 VS Code나 Cursor 같은 데스크톱 IDE에도요.
00:00:44하지만 모두 Xterm.js를 사용하는데,
00:00:47오래전부터 존재해 왔고
00:00:49사실상 기본값이기 때문이죠.
00:00:51하지만 Xterm에는 큰 문제가 있습니다.
00:00:52캔버스 요소에 렌더링한다는 점입니다.
00:00:54그래서 텍스트를 선택하거나
00:00:56페이지 내에서 단어를 찾는 것 같은 작업들이
00:00:58모두 처음부터 다시 구현되어야 하고,
00:01:00항상 매끄럽게 작동하는 건 아니죠.
00:01:02Wterm은 완전히 다른 접근 방식을 취합니다.
00:01:04DOM에 직접 렌더링하는 방식이죠.
00:01:05즉, 터미널 출력이 그냥 HTML이 되므로
00:01:08브라우저가 알아서 처리해 줍니다.
00:01:10Wterm은 이런 HTML 렌더링을 활용해
00:01:13정말 멋진 기능들을 수행할 수 있습니다.
00:01:14매 프레임마다 전체 터미널을 다시 그리는 대신
00:01:17업데이트된 행만 다시 렌더링할 수 있거든요.
00:01:20또한 React나 Vue 같은
00:01:22다양한 프레임워크에서 사용 가능합니다.
00:01:23테마 변경도 가능하고요.
00:01:24별도의 Wterm Ghosty 패키지도 있어서,
00:01:27Zig 코어를 Lib Ghosty로 교체할 수 있는데,
00:01:29다른 웹 Ghosty 프로젝트보다
00:01:31놀랍도록 더 잘 작동합니다.
00:01:33그건 영상 뒷부분에서 더 자세히 다루겠습니다.
00:01:35일단은 간단한 데모 프로젝트로
00:01:37Wterm을 테스트해 보죠.
00:01:38React에 Wterm을 설치한 후,
00:01:40컴포넌트와 CSS를 가져왔고,
00:01:43여기 컴포넌트를 렌더링하고 있습니다.
00:01:45이제 앱을 실행하고 브라우저로 가보면,
00:01:48터미널이 보이는 걸 확인할 수 있습니다.
00:01:49하지만 ls 같은 명령어를 입력해도,
00:01:51아무 일도 일어나지 않죠.
00:01:52이유는 정보를 읽어올
00:01:53다른 컴퓨터와 연결되어 있지 않기 때문입니다.
00:01:56설명해 드릴게요.
00:01:57지금 클라이언트가 백엔드에 연결되지 않아서,
00:01:59정보를 가져올 곳이 없는 상태입니다.
00:02:01그래서 다른 머신에 연결해야 합니다.
00:02:04제 로컬 머신이 될 수도 있고,
00:02:06클라우드상의 머신일 수도 있죠.
00:02:07그 머신 내에서 동일한 크기의
00:02:10가상 터미널이나 의사 터미널(pseudo terminal)을
00:02:13렌더링해야 합니다.
00:02:14키 입력을 하면,
00:02:16그 키 정보가 다른 머신으로 전송되고,
00:02:18거기서 실행된 결과를 렌더링한 다음,
00:02:20그 모든 정보를 클라이언트로 다시 보냅니다.
00:02:22이런 왕복 과정이
00:02:23최소한의 지연 시간으로 아주 빠르게 일어나야 하죠.
00:02:25그래서 클라이언트와 다른 머신을
00:02:26가장 잘 연결하는 방법은 웹소켓을 쓰는 겁니다.
00:02:28한번 해보죠.
00:02:30Node가 설치된 Ubuntu 서버를
00:02:31이미 준비해 두었습니다.
00:02:32서버 스크립트가 있는 Wterm 서버도 있고요.
00:02:34이걸 살펴보면,
00:02:35슬래시 API 슬래시 터미널 경로에
00:02:37웹소켓 서버를 만드는 걸 볼 수 있습니다.
00:02:38나중에 좀 더 자세히 설명하겠지만,
00:02:40여기 아래에서
00:02:42터미널 타입과 일치하는 이름으로
00:02:43의사 터미널을 생성하고 있습니다.
00:02:45궁금하시다면 여기서 자신의 것을 찾을 수 있습니다.
00:02:48그리고 여기 아래에서는,
00:02:49클라이언트로부터 키 입력을 받아
00:02:49서버에서 처리하고,
00:02:51가상 터미널 안에서 실행한 뒤
00:02:53다시 클라이언트로 반환하고 있습니다.
00:02:55서버는 업데이트된 특정 행만이 아니라
00:02:56모든 걸 반환하죠.
00:02:58이제 app.tsx 파일의 클라이언트 쪽을 보면,
00:02:59슬래시 API 슬래시 터미널 경로로
00:03:01웹소켓 연결을 하고 있습니다.
00:03:02그리고 Wterm의 웹소켓 트랜스폼을 사용해서
00:03:03자동 재연결 옵션과 함께
00:03:05그 URL로 연결하죠.
00:03:07이게 여기서 서버로 키 정보를 전송합니다.
00:03:10브라우저 크기 조절도 여기서 처리하고요.
00:03:11그다음 여기, handle data 함수가
00:03:14서버로부터 들어오는 모든 정보를 처리합니다.
00:03:16ZIG 코어의 멋진 점은
00:03:19이 정보를 분석해서 바뀐 부분이 무엇인지 파악하고,
00:03:21그 부분만 HTML로 다시 렌더링한다는 점입니다.
00:03:23여기 열과 행의 크기도
00:03:24서버에서 설정한 것과 맞춰야 합니다.
00:03:26나머지는 직관적이라 이해하기 쉬울 겁니다.
00:03:27자, 이제 클라이언트와 서버가 실행 중이니,
00:03:28브라우저로 돌아와서 ls를 입력해 보면,
00:03:30사용 가능한 파일 목록이 보입니다.
00:03:31ls에 L 옵션을 붙여서
00:03:33파일 정보를 자세히 볼 수도 있고,
00:03:35디렉토리를 이동해서
00:03:36파일 내용을 확인하거나
00:03:39실행 중인 컨테이너 목록을 볼 수도 있죠.
00:03:41Vim으로 파일을 열어서
00:03:42내용을 탐색할 수도 있습니다.
00:03:45물론 작동은 하지만,
00:03:47완벽하게 매끄럽지는 않습니다.
00:03:49예를 들어 텍스트를 드래그해 보면,
00:03:52일부 문자를 전혀 알아볼 수 없습니다.
00:03:53이걸 고치려면
00:03:55Ghosty 코어를 로드하고
00:03:57React prop에 추가해서
00:03:59Wterm을 Ghosty와 설정할 수 있습니다.
00:04:01이제 서버 파일을 열고
00:04:02텍스트를 선택해 보면,
00:04:03훨씬 더 읽기 좋아졌죠.
00:04:04열린 코드를 올바르게 렌더링하고,
00:04:06모델을 전환하며,
00:04:07이모지 지원과 함께 프롬프트를 입력할 수도 있습니다.
00:04:09기본 Wterm 코어 렌더러보다
00:04:10색상 표현도 살짝 더 나은 걸 볼 수 있습니다.
00:04:12하지만 Zig 코어는 12KB인 반면,
00:04:13Ghosty는 400KB입니다.
00:04:15파일 크기가 중요하다면,
00:04:16Zig 코어를 유지하는 게 나을 수도 있습니다.
00:04:18어쨌든 Vercel의 Wterm에 대한 간단한 개요였습니다.
00:04:20물론 다루지 않은 기능이 정말 많습니다.
00:04:22마크다운을 멋진 터미널 출력으로 바꾸거나,
00:04:23백엔드 접근 권한이 없을 때
00:04:24Bash만으로 가상 파일들을 탐색하는 것,
00:04:26심지어 브라우저 터미널을 통해
00:04:27SSH 클라이언트를 설정하는 예제도 있죠.
00:04:29하지만 Wterm이 완벽하다고 생각하진 않습니다.
00:04:31Ghosty 버전을 사용할 때
00:04:31NeoVim이나 OpenCode를 오갈 때 렌더링 문제가 있었고,
00:04:34BUN 프론트엔드에서 Ghosty 렌더러를 작동시키려면
00:04:36Ghosty의 400KB와 비교하면 말이죠.
00:04:39JS가 아닌 파일은 복사하지 않아서
00:04:40직접 가져와야 했거든요.
00:04:43하지만 DOM 렌더링 방식 자체는 마음에 듭니다.
00:04:46따로 애쓰지 않아도 접근성과
00:04:48브라우저 네이티브 기능을
00:04:49그대로 활용할 수 있으니까요.
00:04:51Xterm은 10년이 넘게 지났는데도
00:04:52그걸 구현하느라 고생했거든요.
00:04:55하지만 Xterm.js는 방대한 생태계를 갖춘
00:04:57검증된 솔루션이므로,
00:04:58선택한다고 해서 잘못되는 건 아닙니다.
00:05:00Coda의 GhostyWeb도 있습니다.
00:05:02이건 다른 접근 방식을 취하죠.
00:05:05실제 Ghosty 터미널에 사용된 것과
00:05:06동일한 libGhosty 엔진을 사용하지만,
00:05:08NeoVim을 오가면서
00:05:10혹은 OpenCode를 쓸 때도요.
00:05:11동일한 API를 씁니다.
00:05:13하지만 더 나은 터미널을 사용할 수 있죠.
00:05:15WASM 파일을 가져와야 했습니다
00:05:17BUN은 Node 모듈 폴더에서
00:05:19JS가 아닌 파일은 복사하지 않으니까요.
00:05:21하지만 DOM 렌더링 방식은 마음에 듭니다.
00:05:23따로 작업을 하지 않아도
00:05:25웹 접근성과 네이티브 브라우저 기능을
00:05:27그대로 사용할 수 있으니까요.
00:05:29Xterm은 이 부분에서 고전해 왔죠.
00:05:3110년 넘게 사용되어 왔음에도 말입니다.
00:05:33하지만 Xterm.js는 방대한 생태계를 갖췄고
00:05:35이미 검증된 솔루션이기 때문에
00:05:38선택하셔도 후회는 없을 겁니다.
00:05:40Coda의 GhostyWeb도 있는데
00:05:42이건 접근 방식이 다릅니다.
00:05:43실제 Ghosty 터미널에서 사용하는
00:05:45동일한 libGhosty 엔진을 사용하죠.
00:05:48Xterm의 대체제로 바로 쓸 수 있고
00:05:50여전히 캔버스 렌더링 방식을 사용하며
00:05:52API도 동일합니다.
00:05:54하지만 더 나은 터미널을 경험할 수 있죠.

Key Takeaway

Wterm은 DOM 렌더링 방식을 채택하여 12KB의 가벼운 크기로 웹 터미널의 접근성과 네이티브 브라우저 기능을 대폭 향상했습니다.

Highlights

  • Wterm은 캔버스 대신 DOM에 직접 렌더링하여 텍스트 선택, 브라우저 찾기, 스크린 리더와 완벽하게 호환됩니다.

  • 기본 Zig 기반 코어는 12KB WASM 바이너리로 구성되어 매우 가볍습니다.

  • 업데이트된 터미널 행만 다시 렌더링하는 방식을 사용하여 효율성을 높였습니다.

  • LibGhostty 기반 백엔드를 선택하면 Ghostty 터미널과 동일한 엔진을 브라우저에서 사용할 수 있습니다.

  • Xterm.js는 캔버스 렌더링 방식 때문에 텍스트 선택 등 웹 네이티브 기능 구현에 제약이 있습니다.

Timeline

Wterm의 설계와 기존 Xterm.js의 한계

  • Wterm은 캔버스 렌더링 대신 DOM에 직접 터미널 출력을 렌더링합니다.
  • 기존 표준인 Xterm.js는 캔버스 방식의 한계로 인해 텍스트 선택과 검색 기능 구현에 복잡한 과정이 필요합니다.
  • Wterm은 변경된 행만 다시 렌더링하여 렌더링 성능을 최적화합니다.

대부분의 웹 터미널은 Xterm.js를 사용하지만, 이는 캔버스 기반이라 브라우저 네이티브 기능과의 통합이 어렵습니다. Wterm은 터미널 출력을 HTML로 직접 변환함으로써 브라우저가 텍스트 선택이나 스크린 리더 기능을 자동으로 처리하도록 설계되었습니다.

Wterm의 구동 방식과 서버 연결

  • Wterm은 Zig 코어(12KB) 또는 LibGhostty 코어(400KB)를 백엔드로 선택할 수 있습니다.
  • 원격 머신과 데이터를 주고받기 위해 웹소켓(WebSocket)을 활용합니다.
  • 서버는 클라이언트로부터 키 입력을 받아 의사 터미널(pseudo terminal)에서 실행하고 결과를 클라이언트로 전송합니다.

Wterm 자체는 클라이언트 렌더링 도구일 뿐이므로, 실제 명령을 실행할 로컬이나 클라우드 머신과의 연결이 필수입니다. 서버는 가상 터미널을 생성하고 표준 입출력을 웹소켓을 통해 클라이언트와 동기화하며, 클라이언트는 이 데이터를 받아 필요한 부분만 갱신합니다.

LibGhostty 통합과 기술적 비교

  • 기본 Zig 코어는 파일 크기가 작지만, 복잡한 렌더링 상황에서 가독성 문제가 발생할 수 있습니다.
  • LibGhostty를 통합하면 400KB의 바이너리 크기가 필요하지만, 색상 표현과 렌더링 품질이 더 우수합니다.
  • Xterm.js는 방대한 생태계를 갖춘 검증된 솔루션으로 여전히 유효한 선택지입니다.

Wterm은 경량화를 추구하는 Zig 코어와 성능과 품질을 보장하는 LibGhostty 코어 간의 균형을 사용자가 선택할 수 있게 합니다. 캔버스 렌더링 방식의 Xterm.js나 다른 대안인 GhosttyWeb과 비교했을 때, Wterm의 가장 큰 차별점은 웹 접근성을 극대화한 DOM 렌더링 방식에 있습니다.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video