Linear는 빠르다. 정당한 평가다. 체감 성능에 크게 투자했고, 대부분의 팀에게 이용 가능한 SaaS 이슈 트래커 중 최고다. 하지만 "최고의 SaaS"에는 일부 개발자가 수용할 수 없는 제약이 따른다: 데이터가 남의 서버에 놓이고, 워크플로가 그들의 의견에 맞춰 휘고, 모든 인터랙션에 네트워크 라운드트립 세금이 붙는다.
이 글은 그런 벽에 부딪힌 개발자를 위한 것이다. 시간당 50개 이슈를 발행하는 AI 에이전트 플릿을 관리하고 있을 수 있다. 에어갭이나 오프라인 퍼스트 환경에서 작업하고 있을 수 있다. 단순히 이슈와의 사이에 로그인 화면을 넣고 싶지 않을 수도 있다. 여기서는 모든 것을 로컬에 유지하는 네이티브 데스크톱 이슈 트래커 Beadbox를 구축하면서 배운 것을 공유한다.
관심 있는 섹션으로 바로 이동:
- 로컬 퍼스트 속도 -- CLI와 UI 레벨의 실제 데이터셋 타이밍
- Git 네이티브 히스토리 -- 이슈 데이터베이스를 브랜치, diff, 머지
- 오프라인 / 에어갭 -- 네트워크 없음, 데몬 없음, 문제 없음
- 스크립팅과 에이전트 -- 복사 붙여넣기 가능한 3가지 워크플로
- 트레이드오프 -- 시간을 투자하기 전의 솔직한 제한 사항
- 팀 판단 매트릭스 -- 어떤 도구가 어떤 팀 형태에 맞는가
- Linear에서 마이그레이션 -- 있는 것과 없는 것
개발자가 Linear 대안을 찾는 이유
흔한 답은 "Linear가 너무 독선적이다"이다. 맞지만 부정확하다. Linear는 사이클, 팀 구조, 워크플로 상태를 강제하며 2주 케이던스로 출시하는 프로덕트 팀을 전제한다. 해당된다면 Linear는 훌륭하다. AI 에이전트를 조율하는 솔로 개발자, 비표준 이터레이션 패턴의 연구팀, Slack 스레드가 아닌 git 커밋에 이슈를 연결해야 하는 DevOps 그룹이라면, Linear의 의견은 마찰이 된다.
더 깊은 문제는 아키텍처에 있다. Linear는 클라우드 퍼스트 SaaS 제품이다. 모든 변경이 그들의 서버와의 왕복이다. 모든 쿼리가 그들의 가동 상태에 의존한다. 이슈 데이터가 그들의 데이터베이스에 존재하고, 그들의 API를 통해, 그들의 조건으로 쿼리 가능하다. 대부분의 팀에게 그것은 합리적인 트레이드오프다. 데이터 주권, 오프라인 접근, 대규모 데이터셋에서의 원시 쿼리 속도를 중시하는 개발자에게는 치명적인 문제다.
Beadbox가 못하는 것
Beadbox의 장점에 들어가기 전에, 적합하지 않은 경우를 설명한다. 이 섹션을 건너뛰어도 도움이 안 된다. 도구 도입 후 이런 벽에 부딪히는 게 더 아프다.
멀티 유저 권한이나 접근 제어가 없다. 사용자 계정도, 역할도, 이슈별 가시성 제한도 없다. .beads/ 디렉토리(또는 Dolt 서버)에 파일 시스템 접근이 있는 모든 사람이 전부를 읽고 쓸 수 있다. 누가 무엇을 볼 수 있는지 제한해야 한다면, 현시점의 Beadbox는 맞지 않다.
실시간 협업이 제한적이다. 두 사람이 같은 이슈 세트에서 작업할 수 있지만, 협업 모델은 푸시/풀(Git처럼)이지 라이브 커서와 프레즌스 인디케이터가 아니다. 서버 모드에서는 Beadbox가 3-5초마다 변경을 폴링한다. 임베디드 모드에서는 파일 시스템 워치가 더 빠르게 변경을 감지하지만(서브초), 두 프로세스에서 같은 Dolt 데이터베이스에 동시 쓰기하면 크래시할 수 있다. 안전한 패턴은: 한 번에 한 쓰기 주체, 또는 Dolt가 동시성을 처리하는 서버 모드 사용.
Slack, GitHub, Figma 등 SaaS 도구와의 인테그레이션이 없다. 확장 포인트는 bd CLI와 셸 스크립트다. 워크플로가 "이슈 클로즈 시 Slack 메시지 트리거"에 의존한다면, 그 연결 코드를 직접 만들어야 한다.
스케일 상한이 현실적이지만 멀다. 1만 건과 2만 건 이슈 데이터셋으로 테스트한다(벤치마크 후술). 잘 처리된다. 10만 건 이상에서는 스트레스 테스트하지 않았다. 연간 수십만 건의 이슈를 생성하는 대조직에게는 검증되지 않은 영역이다.
비기술 스테이크홀더 접근이 없다. 웹 포털도, 게스트 뷰어도, 공유 가능한 대시보드 URL도 없다. Beadbox는 로컬 데이터베이스를 읽는 데스크톱 앱이다. 자기 머신을 쓰지 않는 PM에게 진행 상황을 보여주려면 화면 공유나 리포트를 생성하는 bd 스크립트가 필요하다.
Beadbox 동작 원리 (30초 버전)
벤치마크가 의미를 가지려면 아키텍처 설명이 필요하다:
임베디드 모드: Dolt 데이터베이스가 파일 시스템의 .beads/에 존재한다. 서버 프로세스도 데몬도 없다. bd CLI가 직접 읽고 쓴다. Beadbox가 250ms 디바운스로 fs.watch()를 써서 변경을 감지하고 WebSocket을 통해 UI에 브로드캐스트한다. 제로 셋업 경로다.
서버 모드: dolt sql-server 프로세스가 별도로 실행된다(로컬 또는 LAN). bd CLI가 MySQL 프로토콜로 연결한다. Beadbox가 파일 시스템 감시 대신 3-5초마다 서버에 변경을 폴링한다. 이 모드는 여러 동시 쓰기를 지원한다.
GUI가 수행하는 모든 작업은 bd CLI를 통해 라우팅된다. Beadbox는 데이터베이스에 직접 접근하지 않는다. bd show와 Beadbox가 일치하지 않으면 Beadbox의 버그다.
성능: 1만 건 이슈 데이터셋의 실제 벤치마크
beads CLI는 벤치마크를 공개하고 있으며 자체 하드웨어에서 재현할 수 있다. M2 Pro에서 1만 건 이슈 Dolt 데이터베이스에 대해 Go 벤치마크 스위트를 실행한 실제 수치:
| 작업 | 시간 | 메모리 | 데이터셋 |
|---|---|---|---|
| 레디 작업 필터 (블로킹 안 된 이슈) | 30ms | 16.8 MB | 1만 건 |
| 검색 (전체 오픈, 필터 없음) | 12.5ms | 6.3 MB | 1만 건 |
| 이슈 생성 | 2.5ms | 8.9 KB | 1만 건 |
| 이슈 업데이트 (상태 변경) | 18ms | 17 KB | 1만 건 |
| 사이클 감지 (5천 개 선형 체인) | 70ms | 15 KB | 5천 의존 |
| 일괄 클로즈 (100건) | 1.9s | 1.2 MB | 순차 쓰기 |
| 동기 머지 (10 생성 + 10 업데이트) | 29ms | 198 KB | 배치 작업 |
이것은 CLI 레벨 벤치마크다: bd가 로컬 Dolt 데이터베이스를 읽고 쓰는 데 걸리는 시간. Beadbox UI는 그 위에 렌더링 오버헤드를 추가한다. 풀 스택(CLI 호출 + React 렌더링 + WebSocket 전파)의 설계 목표:
| UI 작업 | 설계 목표 |
|---|---|
| 에픽 트리 렌더링 (100건 이상) | < 500ms |
| 필터 적용/해제 | < 200ms |
| 워크스페이스 전환 | < 1초 |
| 실시간 업데이트 전파 (임베디드) | < 2초 |
| 콜드 스타트부터 사용 가능까지 | < 5초 |
Linear나 다른 트래커에 대한 벤치마크는 공개하지 않는다. 통제된 비교를 수행하지 않았고, 편의적 수치 선별은 정직하지 않기 때문이다. 말할 수 있는 것은: 전체 데이터 경로가 로컬이라는 것. 필터 클릭에서 결과 표시까지 네트워크 홉이 없다. 그것이 중요한지는 자신의 기준선에 달렸다. 데이터셋 크기와 위치에 대해 Linear가 충분히 빠르다고 느낀다면, 아마 그럴 것이다. 컨퍼런스 호텔 Wi-Fi에서 500건 백로그에 지연을 느낀 적이 있다면, 이 수치가 해결하는 고통을 알고 있을 것이다.
재현하려면: beads를 클론하고, go test -tags=bench -bench=. -benchmem ./internal/storage/dolt/...를 실행하고, 자체 하드웨어와 비교한다. 캐시된 데이터셋은 /tmp/beads-bench-cache/에 저장된다.
Git 인테그레이션 깊이: 커밋과 이슈 연결을 넘어서
대부분의 이슈 트래커는 Git 인테그레이션을 기능 체크리스트로 다룬다: 커밋 메시지에서 이슈 ID를 언급하면 이슈에 링크가 나타난다. 유용하지만 얕다.
Beadbox는 beads 위에 구축되어 있으며, beads에서는 Git 시맨틱이 나중에 붙인 인테그레이션이 아닌 스토리지 레이어 자체다. 기반이 되는 Dolt 데이터베이스는 구조화된 데이터에 대해 Git의 머클 트리 데이터 모델을 구현한다. 모든 이슈 변경이 커밋이다. 모든 커밋에 부모가 있다. 코드에 사용하는 것과 같은 시맨틱으로 이슈 히스토리에 대해 dolt diff, dolt log, dolt merge를 실행할 수 있다.
실제로 무엇을 의미하는가:
이슈 히스토리가 감사 가능하다. 데이터베이스 자체가 커밋 그래프다. 임의의 두 시점을 diff해서 어떤 이슈의 어떤 필드가 변경되었는지 정확히 확인할 수 있다. 위에 나중에 붙인 "감사 로그 기능"이 아니다. 스토리지 포맷이 감사 추적 자체다.
브랜치가 이슈에도 동작한다. Dolt는 네이티브로 브랜치를 지원한다. 이슈 데이터베이스를 브랜치해서 재구성을 시도하고, 다시 머지하거나 버릴 수 있다.
동기화가 API 호출이 아닌 푸시/풀이다. 멀티 머신 협업이 git push와 git pull처럼 동작한다. API 토큰도, webhook도, OAuth 플로도 없다. Dolt 리모트를 서버(또는 DoltHub)로 향하게 하고 푸시한다. 다른 머신이 풀한다.
충돌에 대해: Dolt는 3웨이 머지를 사용한다(Git과 동일). 두 사람이 같은 이슈의 다른 필드를 편집하면 머지가 자동 해결된다. 같은 필드를 편집하면 Dolt CLI(dolt conflicts resolve)를 통한 수동 해결이 필요한 충돌이 발생한다. Beadbox에는 아직 충돌 해결 UI가 없다. dolt 레벨에서 충돌을 처리한다. 실제로는 각 사람(또는 에이전트)이 별도의 이슈를 작업하는 경우(전형적인 패턴), 충돌은 드물다. 하지만 팀이 빈번하게 같은 이슈를 동시 편집한다면 알아야 할 마찰점이다. Dolt 머지 문서에 해결 워크플로 상세가 있다.
네이티브 렌더링: Tauri에 Node.js를 번들하는 이유
Linear는 브라우저 탭에서 동작한다. Jira, Asana 등 모든 SaaS 트래커도 마찬가지다. 브라우저 탭은 메모리를 놓고 경쟁하고, OS에 의해 서스펜드되고, 프레임 지연을 추가하는 컴포지터를 통해 렌더링된다.
Beadbox는 Tauri로 구축된 네이티브 데스크톱 애플리케이션으로 동작한다. Tauri 앱은 Chromium을 번들하지 않고 OS 네이티브 WebView를 사용하여 일반적으로 작다(Tauri 런타임 자체는 한 자릿수 메가바이트). Beadbox의 번들은 전형적인 Tauri 앱보다 큰 약 160MB이며, 설명할 가치가 있는 의도적인 트레이드오프다.
그중 84MB가 임베디드 Node.js 런타임이다. 사이드카 아키텍처를 사용한다: Tauri가 Next.js 서버를 자식 프로세스로 생성하고, 서버사이드 렌더링, 서버 액션, 실시간 업데이트용 WebSocket 레이어를 처리한다. Tauri WebView가 이 로컬 서버를 가리킨다. 순수 Rust 백엔드 대신 이것을 선택한 이유는, Next.js 생태계가 React Server Components, 서버 액션, UI 레이어에서의 빠른 이터레이션 속도를 제공하기 때문이다. 비용은 번들 크기. 동등한 Electron 앱은 400MB+가 된다. 순수 Rust + Tauri 앱은 10MB 미만이지만 구축에 3배 시간이 걸리고 React 생태계를 잃는다.
브라우저 탭과의 실용적 차이: Beadbox는 다른 47개 브라우저 탭과 메모리를 공유하지 않는 전용 WebView 프로세스에서 렌더링된다. 100개 이상 중첩된 이슈의 에픽 트리 확장, 전체 백로그에 필터 적용, 워크스페이스 간 전환: 렌더러가 리소스를 놓고 경쟁하지 않을 때 이런 작업이 질적으로 다르게 느껴진다.
REST API가 아닌 CLI로 확장
Linear에는 GraphQL API가 있다. 잘 설계되어 있다. 하지만 Linear를 확장한다는 것은 그들의 서버와 통신하고, 그들의 토큰으로 인증하고, 그들의 레이트 리밋을 처리하는 코드를 작성하는 것이다.
Beadbox는 다른 접근을 택한다: bd CLI가 API다. GUI가 수행하는 모든 작업이 bd를 통해 이루어진다. 터미널에서 쓰는 것과 같은 커맨드라인 도구다.
오늘 복사 붙여넣기 가능한 3가지 워크플로:
트리아지 스윕을 위한 일괄 우선순위 업데이트:
# 모든 오픈 버그를 우선순위 1(크리티컬)로 설정
bd list --status=open --type=bug --json | \
jq -r '.[].id' | \
xargs -I{} bd update {} --priority=1
일일 상태 요약 생성:
# 지난 24시간 동안 뭐가 바뀌었나?
echo "=== 오늘 클로즈 ==="
bd list --status=closed --json | \
jq -r '.[] | select(.updated > (now - 86400 | todate)) | "\(.id) \(.title)"'
echo "=== 현재 블로킹 중 ==="
bd blocked --json | \
jq -r '.[] | "\(.id) \(.title) (blocked by: \(.blocked_by | join(", ")))"'
echo "=== 작업 가능 ==="
bd ready --json | jq -r '.[] | "\(.id) [P\(.priority)] \(.title)"'
AI 에이전트가 작업을 생성하고 담당:
# 에이전트가 버그를 발견, 등록, 담당
ISSUE_ID=$(bd create \
--title "Fix race condition in auth middleware" \
--type bug \
--priority 1 \
--json | jq -r '.id')
bd update "$ISSUE_ID" --status=in_progress --assignee=agent-3
# ... 에이전트가 작업 ...
bd update "$ISSUE_ID" --status=closed
bd comments add "$ISSUE_ID" --author agent-3 \
"Fixed in commit abc1234. Root cause: mutex not held during token refresh."
AI 코딩 에이전트(Claude Code, Cursor, Copilot Workspace)를 실행하고 있다면, CLI 명령 실행 방법을 이미 알고 있다. API 클라이언트 라이브러리도, 인증 절차도 없다. Unix 파이프와 셸 스크립트뿐이다.
Beadbox를 사용해보면 에이전트가 워크플로를 실행할 때 실시간으로 시각화된다.
오프라인 퍼스트는 기능이 아니라 아키텍처다
일부 클라우드 트래커가 "오프라인 모드"를 제공하며 최근 데이터를 캐시하고 재연결 시 동기화한다. 그것은 근본적으로 온라인인 아키텍처에 나중에 붙인 기능이다. 장애 모드는 예측 가능하다: 오래된 캐시, 동기화 충돌, 조용히 큐에 들어가 나중에 실패하는 작업.
Beadbox가 오프라인에서 동작하는 이유는 애초에 온라인이었던 적이 없기 때문이다. 임베디드 모드에서는 이슈 데이터베이스 전체가 파일 시스템의 디렉토리다. 서버 프로세스 없음. 데몬 없음. 네트워크 소켓 없음. bd CLI가 그 디렉토리를 읽고 쓴다. Beadbox가 fs.watch()로 감시하고 발견한 것을 렌더링한다.
동기화할 것이 없는 이유는 원격인 것이 없기 때문이다. 나중에 협업하기로 하면, Dolt의 푸시/풀이 명시적이고 가시적인 동기화를 제공한다. 하지만 기본값은 로컬이다. 기본값은 자기 것이다.
보안은 어떤가? 에어갭이나 민감한 환경에서 Beadbox를 평가하고 있다면, 구체적인 보안 태세는 이렇다:
- 저장 시 암호화: Beadbox는
.beads/디렉토리 자체를 암호화하지 않는다. OS 레벨 디스크 암호화(macOS의 FileVault, Linux의 LUKS, Windows의 BitLocker)에 의존한다. 위협 모델이 데이터베이스별 암호화를 요구하면 이것은 갭이다. - 백업:
.beads/디렉토리는 일반 디렉토리다.cp -r,rsync, Time Machine, 또는 리모트로dolt push모두 동작한다. Dolt의 커밋 히스토리는dolt reset으로 실수 변경을 롤백할 수 있음도 의미한다. - 머신에서 나가는 것: 임베디드 모드에서는 제로. 네트워크 호출 없음. 데스크톱 앱에서는 두 가지 선택적 아웃바운드 연결이 존재: Beadbox 업데이트 확인을 위한 GitHub API(설정에서 비활성화 가능), 옵트인 시 PostHog 애널리틱스(기본 비활성화, PII 수집 없음). 둘 다 이슈 데이터를 전송하지 않는다.
에어갭 환경, 기밀 프로젝트, 비행기나 기차에서 작업하는 개발자에게 이것은 있으면 좋은 기능이 아니다. 동작하는 유일한 아키텍처다.
팀에 맞는 도구 선택
만능인 도구는 없다. 솔직한 분석:
Linear를 선택할 때:
- 팀이 10명 이상이고 중앙집중 프로젝트 관리가 필요
- Slack/GitHub/Figma 인테그레이션에 의존
- 비기술 스테이크홀더가 이슈 트래커에 접근해야 함
- 운영 오버헤드 제로의 관리형 인프라가 필요
- 정기 사이클로 출시하는 프로덕트 팀
Beadbox를 선택할 때:
- 데이터 주권을 중시 (이슈가 머신을 떠나지 않음)
- 정기적으로 오프라인이나 네트워크 제한 환경에서 작업
- 프로그래밍 방식으로 이슈를 읽고 써야 하는 AI 에이전트를 관리
- Git 네이티브 이슈 히스토리가 필요 (브랜치, diff, 머지)
- 필요할 때 비주얼 컴패니언이 있는 CLI 퍼스트 워크플로 선호
- 엔터프라이즈 기능이 필요 없는 솔로 개발자나 소규모 팀 (1-10명)
현재 도구를 계속 쓸 때:
- 전환 비용이 겪고 있는 마찰을 초과
- 팀이 현재 트래커의 API에 의존하는 인테그레이션에 투자
- 워크플로가 이미 도구의 의견에 맞음
Linear에서 마이그레이션 (또는 다른 트래커)
솔직히 말한다: 현시점에서 Linear에서 Beadbox로의 자동 마이그레이션 도구는 없다. CSV 임포트 마법사도, API 브릿지도, 상태 매핑 UI도 없다.
새로 시작한다면 문제없다. bd init하고 이슈를 만들기 시작하면 Beadbox가 즉시 인식한다. 마찰 제로.
기존 Linear 프로젝트를 마이그레이션하고 싶다면, 현시점에서 실행 가능한 경로는 스크립트다: Linear의 API(CSV와 API 엑스포트 지원)에서 내보내고, 데이터를 변환하고, 루프에서 bd create로 이슈를 재생성한다. Linear 고유 메타데이터(사이클, 프로젝트 뷰, SLA 타이머)는 잃지만 제목, 설명, 우선순위, 상태는 보존된다. 마이그레이션 스크립트는 주말 프로젝트이지 분기 단위 인테그레이션이 아니다.
수천 건의 이슈와 수년의 히스토리를 가진 팀에게 이것이 충분하지 않다는 것을 안다. 적절한 임포트 파이프라인 구축이 로드맵에 있지만 아직 출시되지 않았다. 마이그레이션 마찰이 주요 우려라면, 구축될 때까지 기다리거나 새로 시작하는 것이 유스 케이스에 허용 가능한지 평가하길 권한다.
시작하기
Beadbox는 베타 기간 동안 무료다. Homebrew로 설치:
brew tap beadbox/cask && brew install --cask beadbox
이미 beads를 사용하고 있다면, Beadbox가 기존 .beads/ 워크스페이스를 자동 감지한다. 앱을 열면 이슈가 거기 있다. 임포트 단계도 계정 생성도 없다.
beads가 처음이라면, Beadbox가 첫 워크스페이스 초기화를 안내한다. 60초 이내에 이슈를 보게 될 것이다.
Beadbox를 다운로드하거나 beads를 확인해서 로컬 퍼스트 이슈 트래킹이 자신의 워크플로에 맞는지 확인해 보길 바란다.