여울 v0.2.0 공개 베타 — 5주 만에 도구가 된 Git GUI
여러 Git 저장소와 워크트리를 한 화면에서 다루는 여울 v0.2.0을 공개합니다. 첫 공개 빌드까지의 5주 기록과 최신 설치 안내를 함께 정리했어요.
여울의 두 번째 공개 베타인 v0.2.0을 배포합니다. 이번 버전은 하나의 저장소를 잘 보는 데서 더 나아가, 여러 저장소와 워크트리를 한 작업 공간에서 끊김 없이 다루는 데 초점을 맞췄습니다.
여울 v0.2.0 DMG 바로 다운로드 — Apple Silicon과 Intel Mac을 모두 지원하는 Universal 빌드입니다. 링크를 누르면 GitHub 릴리스 페이지를 거치지 않고 바로 다운로드됩니다.
여러 저장소가 하나의 작업 공간으로
프로젝트를 구성하는 저장소가 여러 개라면 이제 각각을 따로 열 필요가 없습니다. 작업 공간 트리에서 저장소와 워크트리를 함께 살펴보고, 선택한 저장소의 변경 파일과 커밋 흐름으로 바로 이동할 수 있습니다.

저장소별 변경 개수와 현재 브랜치를 한눈에 확인할 수 있고, 여러 저장소에 흩어진 변경도 같은 흐름에서 스테이지하거나 다시 내릴 수 있습니다.
워크트리를 부가 기능이 아닌 기본 작업 단위로
여울의 중심은 여전히 워크트리입니다. 브랜치와 워크트리 관계를 트리에서 확인하고, 각 워크트리의 변경·히스토리·터미널로 자연스럽게 이어집니다. AI 기능을 앞세우는 도구보다 실제 Git 작업과 워크트리 운용을 편하게 만드는 Git GUI를 목표로 합니다.

v0.2.0에서 달라진 점
- 여러 Git 저장소를 하나의 작업 공간 트리로 통합
- 저장소별 변경 파일과 커밋 히스토리 조회
- 저장소를 넘나드는 스테이지·언스테이지 흐름 개선
- 워크트리와 저장소 상태 배지 정리
- 좁은 창에서도 헤더와 pull·push 상태가 줄바꿈되지 않도록 반응형 레이아웃 개선
- 간격·디바이더·타이포그래피와 테마 설정 정돈
- 외부 파일 변경 감지와 패키지 실행 검증 안정화
설치와 업데이트
- 위 DMG 링크에서 설치 파일을 내려받습니다.
- DMG를 열고 여울을 응용 프로그램 폴더로 옮깁니다.
- 기존 버전을 사용 중이라면 앱을 종료한 뒤 새 버전으로 교체합니다.
앞으로 새 버전은 소스 코드와 분리된 공개 릴리스 저장소에서 확인합니다. 여울이 latest-mac.yml을 통해 새 버전을 찾고 DMG와 SHA-512를 검증한 뒤 설치 파일을 열어줍니다. 소스 저장소는 계속 비공개로 유지됩니다.
공개 베타에서 알아둘 점
현재 Apple Developer ID 서명·공증이 없는 ad-hoc 서명 빌드입니다. 첫 실행에서 macOS가 막으면 Finder에서 여울을 우클릭하고 열기를 선택해야 할 수 있습니다. 중요한 저장소는 원격에 백업한 뒤 사용하는 것을 권장합니다.
문제나 개선 의견은 여울 릴리스 저장소의 Issues에 남겨주세요.
첫 공개 빌드 0.0.1 기록
지금 이 글도 그 앱으로 커밋했습니다.
5주 전에 "화면엔 AI만 남았는데, 커밋은 어디서 하지"로 시작한 이야기예요. 에디터를 안 쓰게 되면서 IntelliJ의 git 도구 하나 때문에 IDE를 켜는 게 이상해졌고, 그래서 직접 만들기로 했던 그 앱이요.
오늘 그 앱이 제 독(Dock)에 아이콘으로 있습니다. 이름은 Git GUI, 버전 0.0.1. 설치 가능한 .dmg가 나왔고, 저는 이제 매일 이걸로 커밋합니다.
완성이라고는 안 할게요. 다만 "만들고 있는 것"에서 "쓰고 있는 것"으로 넘어갔습니다. 그 선을 넘은 기록이에요.

Git GUI 0.0.1 — 5주 만에 손에 쥔 첫 릴리스.
이 앱으로 보내는 하루
기능을 나열하면 읽히지 않으니, 제가 실제로 쓰는 순서대로 적어볼게요.

왼쪽은 변경 목록과 커밋 폼, 가운데는 diff, 오른쪽은 커밋 히스토리. 실제 arcaC 저장소를 연 화면이에요.
아침에 앱을 켜면 어제 열어둔 저장소들이 탭으로 그대로 돌아옵니다. 창 배치도, 사이드 접힘도, 터미널 높이도요. 회사 저장소 탭에서 작업하다가 개인 프로젝트는 ⌘T로 새 탭에 엽니다.
왼쪽 [브랜치] 탭은 접이식 트리예요. feature/로 시작하는 브랜치들이 폴더로 묶여 있고, 이름 옆에 초록 ↑와 주황 ↓가 조용히 떠 있습니다. 올릴 게 있다는 뜻, 받을 게 있다는 뜻.
브랜치를 두 번 클릭하면 오른쪽 히스토리가 그 계보로 바뀌어요. 로컬·원격(☁)·태그(🏷)가 한 그래프에 있고 "지금 여기" 마커가 HEAD를 따라다닙니다.

codex·claude·ooo 같은 접두사가 폴더로 묶이고, 이름 옆 초록 ↑와 주황 ↓가 올릴 것·받을 것을 알려줘요. ➤가 지금 있는 자리.
작업하다 커밋할 게 생기면 왼쪽 [변경] 탭에서 체크박스로 골라 올리고, 컴포저에 메시지 쓰고 ⌘↵. 브랜치를 갈아타야 하는데 저장 안 한 게 있으면 앱이 알아서 치워뒀다가 다시 꺼내줍니다.
충돌이 나면 화면이 [변경] 탭으로 저절로 이동하고, 겹친 부분이 카드 한 장씩 나옵니다. "두 버전이 같은 곳을 다르게 고쳤어요. 어느 쪽을 쓸까요?"
가끔은 그냥 터미널이 편할 때가 있어요. ⌘`를 누르면 아래에서 진짜 쉘이 올라옵니다(node-pty). 거기서 커밋을 하면 위쪽 화면이 알아서 따라 갱신되고요. 앱이 모르는 사이에 저장소가 바뀌는 일이 없습니다.

아래에서 올라오는 건 흉내가 아니라 진짜 쉘이에요. 여기서 커밋하면 위 화면이 알아서 따라옵니다.
리뷰 요청도 앱에서 만듭니다. 열린 PR 목록을 보고, 코멘트에 답하고, 승인하고, 병합하면 기본 브랜치로 이동해서 받아올지 물어봐요.
이게 5주 전엔 없던 하루입니다.
5주 동안 만들어진 것
| 항목 | 수치 |
|---|---|
| 기간 | 2026-07-15 ~ 08-19 (5주) |
| 커밋 | 691개 |
| 설계 스펙 문서 | 27개 (에픽 E0~E15e) |
| 소스 파일 | 178개 (TS/TSX) |
| 테스트 파일 | 60개 |
| 워크스페이스 패키지 | 6개 |
| 패키징 | electron-builder — .app/.dmg 생성·검증 완료 |
패키지 경계는 첫 편에서 정한 그대로예요. domain(순수 TS)·git-adapter(porcelain v2 파싱)·git-process(실행·취소·스트리밍)·ipc-contract(타입 계약)·hosting(GitHub)·desktop(화면). 5주 동안 이 경계를 한 번도 안 고쳤습니다.
0단계에서 민짜 HTML로 수직 한 줄부터 뚫었던 것이 여기서 값을 했어요. 화면은 그 뒤로 세 번쯤 갈아엎었는데, 파서와 도메인은 그대로였거든요.
27개의 스펙이 곧 개발 로그였습니다
5주에 691커밋이면 빠른 편인데, 비결은 손이 빨라서가 아니에요. 에픽 하나마다 같은 루프를 돌았습니다.
스펙 작성 → 구현 → 리뷰(실측) → 스펙 정정 → 머지
· 스펙에는 "무엇을 만든다"가 아니라
"왜 지금 이걸 하는가 + 뭘 감수하는가"를 적는다
· 리뷰는 코드가 아니라 실제 동작을 측정한다
· 실측이 스펙과 다르면 코드가 아니라 스펙을 고친다커밋 로그를 보면 `docs: E14c 스펙 정정 — Task 1 실측` 같은 게 반복돼요. 만들어보고 나서 설계 문서를 고친 흔적입니다. 처음 쓴 스펙이 맞았던 적이 별로 없거든요.
이 루프가 쌓이니 27개 스펙이 그 자체로 개발 일지가 됐어요. "그때 왜 그렇게 했지?"를 다시 물을 일이 거의 없었습니다.
가장 어려웠던 세 순간
1. 네이티브 탭을 포기한 날
저장소를 여러 개 열고 싶어서 macOS 네이티브 탭을 쓰려고 했어요. OS가 그려주니까 공짜라고 생각했죠.
구현 불가로 닫혔습니다. Electron에서 네이티브 탭은 표준 타이틀바에서만 켜지는데, 이 앱은 이미 헤더를 한 줄로 만들어 신호등 버튼과 같은 줄에 놓은 상태였거든요. 양자택일이었어요.
| 선택지 | 얻는 것 | 잃는 것 |
|---|---|---|
| 네이티브 탭 | OS가 그려줌 · 공짜 | 한 줄 헤더 포기, 세로 공간 낭비 |
| 탭바 직접 그리기 | 헤더 유지 | 탭바·드래그·복원을 전부 구현 |
헤더를 지키는 쪽을 골랐습니다. 그래서 탭바를 창 맨 윗줄에 직접 그렸고, 그게 타이틀바 역할까지 겸해요. 탭 순서 바꾸기, 창 밖으로 떼어내 새 창 만들기, 창 사이로 옮기기까지 전부 손으로 만들었습니다.
OS가 해주던 걸 직접 하기로 하면, 그 뒤에 딸려오는 일이 생각보다 훨씬 많아요. 그런데 화면 한 줄이 매일 눈에 보이니까요. 후회는 안 합니다.
2. 15초에 크래시 157번
탭을 만들고 나서 리뷰가 잡아낸 문제예요. 탭 하나가 죽으면(렌더러 크래시) 창 전체가 인질이 됩니다.
탭바를 각 탭이 자기 안에 그리기 때문이에요. 활성 탭이 죽으면 탭바도 단축키도 그 죽은 화면 안에 있어서, 살아 있는 옆 탭으로 갈 방법이 없습니다. 유일한 출구가 창 닫기인데 그러면 멀쩡한 탭들까지 같이 잃고요.
그래서 죽으면 자동으로 다시 켜지게 했습니다. 그리고 실측에서 이게 나왔어요.
연속 자동 재시동 → 무한 루프
실측: 15초에 크래시 157회
"로드하면 반드시 죽는" 렌더러라면
reload 완료가 곧장 다음 크래시를 부른다처방은 상한이었어요. 연속 3회까지만 자동 재시동하고, 로드 후 10초를 살아남으면 다음 크래시는 새 사건으로 세서 카운터를 리셋합니다. 상한을 넘기면 멈추고 죽은 표시를 남겨요.
무한 루프보다 정지가 낫다 — 자동 복구를 만들 때는 항상 이 문장이 필요하더라고요. 복구가 사고를 증폭시키는 경우가 실제로 있으니까요.
3. 한 달간 조용히 깨져 있던 것
제일 오싹했던 건 이거예요. 사이드바를 접으면 터미널 폭이 안 맞는 문제가 있었는데, 원인이 한 달 전 코드였습니다.
// 마운트 시점 상태를 굳혀버린 이펙트
useEffect(() => {
// ... sessions 를 참조하지만 deps 는 []
}, []);
// 결과: refitActive 가 항상 activeId: null 로 호출
// 실측 17회 시도 → fit 0회창 크기를 바꿀 때는 다른 코드의 부수효과가 이걸 가려줘서 멀쩡해 보였어요. 사이드 접기에서만 드러났습니다. 도크는 1160px인데 터미널은 737px, 오른쪽 35%가 빈 공간이었죠.
이걸 계기로 이 저장소 첫 lint 게이트를 넣었습니다. React 훅 규칙을 error로 올리고, 억제해둔 12곳을 전부 해소했어요. 억제 주석마다 "왜 지금은 못 푸는지 + 뭘 바꾸면 풀리는지"를 적어뒀더니, 그 주석들이 그대로 다음 에픽의 할 일 목록이 되더라고요.
규칙을 기계에 맡기는 이야기를 회사 코드베이스에서 그렇게 했는데, 개인 프로젝트에서도 결국 같은 결론에 도착했습니다.
아직 안 된 것들
0.0.1이라고 붙인 이유가 있어요.
| 남은 것 | 상태 |
|---|---|
| 취소 가능한 Git 프로세스·실행 로그 | 미착수 — 긴 작업 중단이 아직 안 됨 |
| 네트워크 진행 표시 | 미착수 |
| 충돌·중단 상태별 테스트 fixture | 일부만 |
| Windows·Linux | 미지원 — macOS만 패키징 |
| submodule · LFS · sparse checkout | 설계에는 있고 구현은 아직 |
제품 목적 문서에는 submodule과 LFS도 1급으로 다루겠다고 써놨는데, 아직 못 했습니다. 쓰면서 아쉬운 순서대로 하는 중이에요.
그리고 정직하게 — 이건 아직 남에게 권할 만큼 검증된 앱은 아닙니다. 제 저장소들에서 매일 도는 정도고요.
결국 만든 건 초보용 앱이 아니었어요
이 시리즈를 시작할 때 목표는 "Git을 모르는 사람도 쓸 수 있는 앱"이었습니다. 그런데 3편에서 "저장하기"를 버리고 "커밋"으로 돌아왔고, 결국 개발자 어휘가 기본이 됐어요.
만들면서 알게 된 게 있어요. 제가 만들 수 있는 건 "내가 매일 쓰는 앱"이지 "남을 위한 앱"이 아니라는 것. 매일 쓰지 않으면 뭐가 불편한지 모르고, 모르면 못 고칩니다.
쉬운 말은 버린 게 아니라 미뤄뒀어요. 용어가 한 파일에 모여 있어서 표 하나만 더 만들면 되살아납니다. 다만 그건 이 앱이 저한테 충분히 좋아진 다음의 일이에요.
충돌을 카드로 만들고, 터미널을 넣고, 어휘를 바꾸고 — 그렇게 도구가 됐습니다. 이 시리즈는 여기서 닫아요.
다음은 만드는 이야기가 아니라 쓰는 이야기가 될 것 같습니다. 매일 쓰다 보면 또 불편한 게 나올 테니까요.


