콘텐츠로 이동

Technology Stack

Umbra 의 기술 스택은 "학습 곡선을 낮추면서도 결제 SaaS 의 안정성을 타협하지 않는 선택" 이라는 기준으로 구성되었습니다. 이 문서는 각 기술의 역할과 선택 이유, 그리고 거부된 대안을 정리합니다.

Why this stack

Umbra 의 스택 선택을 관통하는 네 가지 기준입니다.

첫째, 페어 개발 가능성 입니다. Umbra 는 Pablo 의 설계와 진욱의 구현이 결합된 프로젝트입니다. 진욱이 빠르게 적응할 수 있는 언어와 생태계여야 하며, 이것이 Rust 기반 luxtra-bot 을 폐기하고 Go 로 전환한 결정적 이유입니다.

둘째, 결제 SaaS 의 안정성 입니다. 학습 곡선을 낮춘다고 장난감 수준의 도구를 쓸 수는 없습니다. 결제, 권한, 복구 같은 비즈니스 크리티컬 영역에는 검증된 라이브러리와 패턴을 사용합니다.

셋째, 언어 무관 도메인 모델 입니다. Umbra 는 진욱의 Rust 숙련도가 쌓인 후 Nabi 런타임 위에서 Rust 로 재작성될 예정입니다. 따라서 도메인 모델은 Go 특화 패턴(Go channel, errgroup)에 종속되지 않게 설계하며, 이는 어댑터 레이어에만 허용됩니다.

넷째, 한국 시장 적합성 입니다. 결제는 Toss Payments, 인프라는 한국 리전을 지원하는 Fly.io / Neon / Vercel 을 선택합니다.

Language & Runtime

Layer Choice Version
Backend language Go 1.23
Frontend language TypeScript 5.x
Frontend runtime Bun latest
Discord library disgo v0.19+ (Components V2)

Go 는 concurrency 모델, 표준 라이브러리 완성도, 빠른 컴파일, 낮은 학습 곡선을 갖추었습니다. Rust 대비 추상화 비용을 포기하는 대신 페어 개발 속도를 얻습니다. 상세는 adr/0001-language-go.md 를 참조하세요.

Bun 은 Node 대비 빠른 설치와 실행으로 프론트엔드 DX 를 개선합니다. 패키지 매니저, 번들러, 테스트 러너 역할을 통합합니다.

disgo 는 Go 의 Discord 라이브러리 중 유일하게 Components V2 를 정식 지원합니다. discordgo 는 유지보수가 정체되어 있어 거부했습니다. 상세는 adr/0002-discord-library-disgo.md 를 참조하세요.

Web Framework

Layer Choice
HTTP framework Echo v4
Frontend framework React 19
Routing TanStack Router (code-based)
UI components shadcn/ui + Tailwind CSS
Icon Lucide React

Echo 는 CSRF, Rate Limit, Security Headers 같은 보안 미들웨어를 내장하여 결제 SaaS 의 안전 기본값을 빠르게 확보합니다. Gin 은 CSRF 공식 미들웨어 부재와 컨벤션 차이로 거부, chi 는 필요한 미들웨어 조합 부담으로 거부. 상세는 adr/0003-http-framework-echo.md 를 참조하세요.

React 19 는 생태계 성숙도와 shadcn/ui 통합을 고려한 선택입니다. 프로젝트 특성상 SSR 이 불필요하므로 Next.js 대신 Vite + React SPA 로 단순화합니다.

TanStack Router 는 타입 안전성이 가장 강력한 React 라우팅 라이브러리입니다. 코드 기반 모드로 사용하여 파일 기반 자동 생성 파일의 부담을 제거합니다. 상세는 adr/0028-tanstack-router-query.md 를 참조하세요.

shadcn/ui 는 Radix UI 기반의 접근성과 Tailwind 기반의 디자인 토큰 적용성을 함께 얻습니다. npm 패키지가 아니라 프로젝트에 직접 복사되는 방식이라 커스터마이징 자유도가 높습니다.

Data

Layer Choice
Database Neon Serverless PostgreSQL
DB driver pgx/v5 + pgxpool
DB layer sqlc
Migration Atlas
Cache / Session / Queue backend Redis
PK type UUID v7

Neon 은 serverless PostgreSQL 로 branching 기능을 제공합니다. PR 별 DB 브랜치 생성 → 마이그레이션 검증 → PR merge 시 main 브랜치 적용 흐름이 자연스럽습니다. 상세는 adr/0004-database-neon-postgres.md 를 참조하세요.

pgx 는 Go 의 PostgreSQL 드라이버 중 가장 빠르고 기능이 완전합니다. database/sql 인터페이스도 제공하지만 Umbra 는 네이티브 pgx 인터페이스를 사용합니다.

sqlc 는 SQL 에서 타입 안전한 Go 코드를 생성합니다. 쿼리 가시성 100%, ORM 의 비효율적 쿼리 생성 없음, Atlas 와의 책임 분리 명확성을 갖춥니다. GORM 은 결제 도메인의 트랜잭션 제어 위험으로 거부했습니다. 상세는 adr/0005-db-layer-sqlc.md 를 참조하세요.

Atlas 는 선언형 스키마 관리 도구입니다. db/schema/ 를 source of truth 로 두고 마이그레이션을 자동 생성합니다. golang-migrate 대비 schema drift detection 이 표준으로 내장됩니다. 상세는 adr/0006-migration-atlas.md 를 참조하세요.

Redis 는 세션, asynq 백엔드, idempotency 체크 세 용도로 사용됩니다. 상세는 adr/0007-cache-queue-redis.md 를 참조하세요.

PK 는 UUID v7 (RFC 9562) 을 채택합니다. 시간 정렬 가능성, PostgreSQL uuid 타입 네이티브 지원, 국제 표준 준수가 이유입니다. 상세는 adr/0021-pk-uuid-v7.md 를 참조하세요.

Workflow & Background Jobs

Layer Choice
Workflow engine Temporal (Recovery 전용)
Job queue asynq (Redis 기반)
Outbox poller 자체 구현, worker 프로세스 내 고루틴

Temporal 은 Recovery 와 AntiNuke 워크플로우에만 사용됩니다. 장기 실행, 장애 복원, 시그널 기반 조정이 필요한 영역입니다. 상세는 adr/0014-recovery-temporal-workflow.md 를 참조하세요.

asynq 는 Redis 기반 Go 작업 큐로 짧은 비동기 작업(결제 cron, 재시도, Live Sync 배치, 알림)을 담당합니다. River 는 PostgreSQL 기반이라 Redis 를 이미 쓰는 환경에 불필요한 중복 인프라가 됩니다. 상세는 adr/0015-asynq-vs-temporal-split.md 를 참조하세요.

Outbox poller 는 PostgreSQL events.outbox 테이블을 2초 주기로 폴링하여 도메인 이벤트를 구독자에 전달합니다. platform/event/ 에 구현됩니다. 상세는 adr/0016-outbox-pattern.md 를 참조하세요.

External Services

Layer Choice
Payment Toss Payments (Billing API)
Discord Discord REST + Gateway API
Email 미정 (Phase 2)

Toss Payments 는 한국 시장 표준이며 Billing API (정기결제 빌링키 기반) 를 제공합니다. 공식 Go SDK 가 없어 자체 HTTP 클라이언트를 engine/billing/adapter/toss/ 에 구현합니다. 빌링키는 AES-256-GCM 으로 암호화되어 DB 에 저장됩니다. 상세는 adr/0013-payment-toss-billing.md 를 참조하세요.

Observability

Layer Choice
Logging slog (Go 표준)
Tracing OpenTelemetry
Metrics OpenTelemetry
Backend Grafana Cloud

slog 는 Go 1.21+ 표준 구조화 로그 라이브러리입니다. 외부 의존성 없이 JSON 구조 로그를 제공합니다.

OpenTelemetry 는 trace / metric / log 를 통합하는 벤더 중립 표준입니다. Grafana Cloud 는 무료 tier 에서 세 종류 데이터를 모두 수용하며 Fly.io 와 통합이 용이합니다.

Development Tooling

Layer Choice
Go linter golangci-lint
Go formatter gofumpt
Frontend linter + formatter Biome
Dev reload air (Go), vite dev (Frontend)
Tool version management mise
Task orchestrator just (justfile)
OpenAPI spec generation swag
OpenAPI TS types openapi-typescript
Config management envconfig

모든 개발·DB·코드 생성·린트·테스트 태스크는 루트 justfile 로 오케스트레이션합니다. make 는 사용하지 않습니다 — just 가 더 단순한 문법과 cross-platform 호환성을 제공합니다. package.json 은 Bun workspace 선언 전용이며 orchestration scripts 는 넣지 않습니다.

API 타입 동기화는 swag 로 Echo 핸들러에서 OpenAPI spec 을 생성한 뒤 openapi-typescript 로 프론트엔드 TS 타입을 자동 생성하는 흐름입니다. 자동 생성 파일은 commit 되어 CI 에서 drift detection 에 사용됩니다. 상세는 adr/0030-openapi-type-sync.md 를 참조하세요.

State Management (Frontend)

Layer Choice
Server state TanStack Query
Client state Zustand
Form React Hook Form + Zod
HTTP client Native fetch + custom wrapper

TanStack Query 는 TanStack Router 와 같은 팀이 개발하여 통합이 자연스럽습니다. Server state 는 전적으로 이곳에서 관리하며 Zustand 에 넣지 않습니다.

Zustand 는 전역 UI 상태만 관리합니다. Provider 없이 hook 으로 접근 가능하며 Redux 대비 보일러플레이트가 적습니다.

React Hook Form + Zod 는 표준 조합으로, shadcn/ui Form 컴포넌트가 이 조합을 1급 지원합니다.

HTTP 클라이언트로 axios 는 사용하지 않습니다. fetch + 30줄 래퍼로 충분합니다. TanStack Query 가 retry, caching, error handling 을 모두 처리합니다.

Deployment

Layer Choice
Backend hosting Fly.io
Frontend hosting Vercel
DNS Cloudflare
CI/CD GitHub Actions
Container Distroless multi-stage Docker

Fly.io 는 Discord Gateway 같은 long-running 프로세스를 지원하며 asia-northeast1 리전을 제공합니다. Cloud Run 은 stateless 전용이라 Bot 프로세스에 부적합합니다. 상세는 adr/0010-deploy-fly-vercel.md 를 참조하세요.

Vercel 은 SPA 정적 호스팅과 PR preview 에 최적화되어 있습니다.

Monorepo

Layer Choice
Package manager Bun workspace
Go module 단일 go.mod (github.com/luxtradev/Umbra)
Repo structure apps/*, engine/*, platform/*, db/, docs/

Bun workspace 와 단일 Go module 로 구성된 단일 레포 구조입니다. Bun 은 apps/web/ 의 TypeScript 영역을 관리하고, 단일 go.mod 가 Go 프로세스(bot/api/worker)와 도메인 패키지(engine/*, platform/*)를 모두 포함합니다. go.work 는 도입하지 않습니다. 상세는 adr/0009-monorepo-bun-workspace.md 를 참조하세요.

Summary table

전체 스택을 한눈에 보기 위한 요약입니다.

Category Tools
Language Go 1.23, TypeScript 5.x
Runtime Bun (frontend)
Discord disgo
Web Echo, React 19, TanStack Router, TanStack Query, Zustand, RHF + Zod, shadcn/ui, Tailwind, Lucide
Data Neon PostgreSQL, pgx/v5, sqlc, Atlas, Redis, UUID v7
Workflow Temporal (Recovery only), asynq, Outbox
External Toss Payments, Discord API
Observability slog, OpenTelemetry, Grafana Cloud
Dev tools golangci-lint, gofumpt, air, mise, swag, openapi-typescript, envconfig, Biome, just
Deploy Fly.io, Vercel, Cloudflare, GitHub Actions, Distroless Docker
Monorepo Bun workspace + 단일 go.mod

Constraints

이 스택이 전제하는 조건입니다.

  • 결제 SaaS 규모의 트래픽 — 초대형 트래픽(예: 수백만 TPS)에는 별도 아키텍처 필요
  • 한국 리전 중심 — 글로벌 분산 필요 시 Fly.io 멀티 리전 + Neon read replica 추가 검토
  • 소규모 팀 — Pablo + 진욱 + 향후 소수 합류. 10인 이상 팀은 조직 구조에 맞게 재검토
  • Discord 전용 — Slack, Matrix 등 다른 플랫폼 추가 시 Bot 레이어 재설계

See also

  • architecture/overview.md — 시스템 아키텍처 전체
  • architecture/deployment.md — 배포 토폴로지 상세
  • adr/ — 각 기술 선택의 상세 결정 기록
  • guides/backend-conventions.md — Go 코딩 컨벤션
  • guides/frontend-conventions.md — React 코딩 컨벤션