콘텐츠로 이동

ADR-0009: Monorepo: Bun workspace (single go.mod)

Umbra 는 단일 레포에 Go 프로세스와 TypeScript 웹을 함께 두는 모노레포 구조이며, TypeScript 는 Bun workspace 로, Go 는 단일 go.mod 로 관리한다. go.work 는 도입하지 않는다.

Status

Accepted

  • Decided at — 2026-04-13
  • Decided by — Pablo

Context

Umbra 는 네 개의 프로세스로 구성된다.

  • Go 프로세스 3개 — bot, api, worker
  • TypeScript 웹 1개 — apps/web/

Go 프로세스들은 engine/*platform/* 패키지를 공유하며 단일 go.mod 를 사용한다. TypeScript 웹은 독립된 package.json 을 갖지만 루트 차원의 스크립트, lint, 타입 공유가 필요하다.

결정해야 할 것:

  • 모노레포 vs 멀티 레포
  • 모노레포 내 JS/TS 패키지 매니저
  • 빌드 orchestrator 필요 여부

Decision

단일 레포 (모노레포) 로 운영하며 다음 구조를 사용한다.

  • Go module — 루트의 단일 go.mod (github.com/luxtradev/Umbra). go.work 는 도입하지 않는다 — 단일 module 만으로 engine/*, platform/*, apps/* 의 cross-package import 가 모두 해결된다
  • JS workspacepackage.jsonworkspaces: ["apps/web"] 로 Bun workspace 구성
  • Script orchestrationjustfile 이 단일 오케스트레이터 (recipes: dev-bot, dev-api, dev-worker, dev-web, db-migrate, sqlc, openapi, lint, test)
  • Build orchestrator — 사용 안 함. Turborepo / Nx 도입 안 함

선택 근거:

  • 도메인 공유engine/* 을 세 Go 프로세스가 함께 쓰므로 모노레포가 필수
  • 단일 go.mod 의 단순성 — multi-module + go.work 는 의존성 그래프가 복잡해지고 IDE/도구 호환성 이슈가 늘어난다. 단일 module 은 import path 가 직관적이고 go test ./... 한 번으로 전체 검사 가능
  • Bun 속도 — npm 대비 install/빌드가 빠름. Luxtra 팀의 다른 프로젝트에도 확산 검토
  • 단일성 우선 — 외부 도구(Turborepo, Nx) 없이 Bun scripts + justfile 로 충분. 오버엔지니어링 회피
  • 경계 명확 — Go internal package convention 으로 프로세스 경계 강제 (ADR-0017, ADR-0018)

Consequences

Positive

  • 한 PR 로 프론트엔드와 백엔드를 같이 변경 가능 → 기능 개발 속도 ↑
  • engine/* 공유로 도메인 코드 중복 없음
  • Bun install 속도로 CI 시간 단축
  • 구조 단순 (빌드 orchestrator 학습 부담 0)

Negative

  • 레포가 커질수록 clone/checkout 시간 증가
  • 프론트와 백엔드 기여자가 섞이면 코드 리뷰 부담 증가
  • Luxtra 팀 다른 프로젝트(luxtra-bot, luxtra.dev)는 pnpm 이라 컨벤션 차이

Neutral

  • Bun lockfile (bun.lockb) 은 binary 라 diff 확인이 text 대비 불편 — bun install --save-text-lockfile 옵션 검토
  • Turborepo 없이도 cache 가 필요해지면 추후 도입 가능

Alternatives considered

Alternative 1: 멀티 레포 (Go 와 TS 분리)

Pros

  • 각 레포가 가볍고 단순
  • 소유권 분리 명확

Cons

  • 도메인 코드 공유 시 git submodule 또는 퍼블리싱 필요
  • PR 이 두 레포에 걸치면 조정 부담
  • CI/CD 파이프라인 중복

Why rejectedengine/* 공유로 인한 이점이 멀티 레포 이점 압도.

Alternative 2: pnpm workspace (Luxtra 팀 컨벤션)

Pros

  • Luxtra 다른 프로젝트와 도구 일치

Cons

  • Bun 대비 install 속도 느림
  • Bun 이 이미 runtime 으로 선택됨(ADR-0008)이라 Bun workspace 사용이 자연스러움

Why rejected — Bun workspace 가 Bun runtime 과 통합이 강함. Luxtra 컨벤션 차이는 감수.

Alternative 3: Turborepo

Pros

  • 빌드 캐시, task orchestration
  • PR 속도 향상

Cons

  • 학습 부담 (config, pipeline 정의)
  • Umbra 규모(2개 워크스페이스)에서 과한 도구
  • Bun scripts 로 충분히 대체 가능

Why rejected — MVP 규모에 과함. 레포가 커지면 도입 재검토.

Alternative 4: Nx

Pros

  • 강력한 빌드 캐시, plugin 생태계

Cons

  • Turborepo 보다 더 무거움
  • Angular/NestJS 컨벤션이 강해 React/Go 와 이질

Why rejected — 과도한 복잡도.

Compliance

  • 루트 justfile 의 recipes 에 프로세스별 명령 정의 (dev-bot, dev-api, dev-worker, dev-web, lint-go, lint-web, db-migrate, openapi 등)
  • 루트 package.json 은 Bun workspace 선언 전용 (scripts 비움)
  • 프론트엔드 의존성 추가는 bun --cwd apps/web add <pkg> 사용
  • Go module 은 단일 go.mod 만 사용. go.work 는 도입하지 않음 (도입 시 별도 ADR 로 재논의)
  • 빌드 orchestrator(Turborepo 등) 도입은 ADR 로 재논의

Revisit triggers

  • 워크스페이스가 5개 이상으로 늘어나면 Turborepo 검토
  • 빌드 시간이 CI 에서 10분 이상으로 길어지면 캐시 도구 재평가
  • Luxtra 전사 컨벤션이 Bun 으로 통일되지 않고 혼용이 문제가 되면 재검토
  • Go 도메인이 별도 module 로 분리될 필요 (예: 외부 SDK 공개) 가 생기면 go.work 도입 ADR 작성

References