콘텐츠로 이동

ADR-0011: Hybrid License Model

Umbra 는 결제 주체는 User, 권한 적용 대상은 Guild 로 분리된 Hybrid 멀티테넌시 모델을 채택한다. 한 User 가 여러 길드의 구독을 독립적으로 관리할 수 있다.

Status

Accepted

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

Context

SaaS Discord 봇에서 구독의 주체를 누구로 둘 것인가는 비즈니스 모델의 핵심이다.

현실 관찰:

  • 한 사람이 여러 길드의 owner 또는 관리자 인 경우가 흔하다 (게임 길드, 크리에이터 팬덤, 커뮤니티 네트워크)
  • 길드 owner 가 자주 바뀔 수 있다 (양도, 위임)
  • 결제 수단은 개인 카드 기반이 일반적이다 (한국 사업자 카드는 드물다)

단순 모델의 한계:

  • Guild = Tenant 모델 — 한 사람이 여러 길드 운영 시 각 길드마다 결제 수단 재등록. 관리 부담 ↑
  • User = Tenant 모델 — 개인이 Pro 구독하면 어느 길드든 Pro 적용. 명확하지만 길드 owner 가 바뀔 때 혼란

Decision

Hybrid 모델 을 채택한다.

  • Payer (결제 주체) = User — 빌링키 보유자, 구독 비용 지불
  • Target (권한 적용 대상) = Guild — 혜택이 적용되는 단위
  • One user, many subscriptions — 한 User 가 여러 Guild 의 구독을 독립적으로 관리

즉 Subscription 은 항상 (payer_user_id, guild_id) 쌍을 가진다. 빌링키 하나로 여러 구독을 동시 운영 가능하고, 각 구독은 자기 Guild 에만 혜택을 적용한다.

선택 근거:

  • 실제 사용 패턴 부합 — 한 사람이 여러 길드를 운영하는 현실 반영
  • 결제 수단 일원화 — 같은 사람이 여러 길드 결제 시 빌링키 재등록 불필요
  • Owner 이양 영향 최소화 — 길드 owner 가 바뀌어도 결제는 이전 owner 가 유지 (해지 전까지)
  • 명확한 혜택 적용 — 권한은 Guild 단위라 "내 구독이 어느 길드에 적용되는지" 모호함 없음

Consequences

Positive

  • 여러 길드를 운영하는 power user 에게 UX 우호적
  • 결제 수단 등록 횟수 ↓
  • "내 대시보드에서 여러 길드 구독 통합 조회" 같은 기능이 자연스러움
  • 결제 수단(User) 과 혜택(Guild)의 lifecycle 독립 관리

Negative

  • 한 Guild 에 여러 User 가 동시에 구독 시도 가능 (unique constraint 로 방지 필요)
  • Guild Owner 가 바뀔 때 기존 구독 처리 정책 필요
  • License 와 Subscription 을 분리해야 함 (ADR-0012)
  • 구독 모델을 단순 "User 1인 소유" 으로 여기는 사람에게 학습 곡선

Neutral

  • 데이터 모델은 Payment 업계의 표준 패턴 (Stripe, Paddle 의 "customer + subscription" 과 유사)
  • Toss 의 customerKey 와 자연스럽게 매핑

Decision details

Who can subscribe a Guild

Guild 에 구독을 시작할 수 있는 User 는 다음을 만족해야 한다.

  • Discord 에서 해당 Guild 의 Manage Server 권한 보유
  • Umbra 에 등록된 User (Discord OAuth2 로그인 완료)

Owner 만으로 제한하지 않는다 — 대형 커뮤니티는 운영진이 실무를 담당하는 경우가 많다.

Subscription uniqueness

한 Guild 에는 active subscription 이 최대 1개 만 존재한다.

  • DB 제약: UNIQUE (guild_id) WHERE status IN ('active', 'past_due')
  • 이미 active 구독이 있으면 다른 User 의 신규 구독 시도는 차단
  • 해지 후 재구독은 새 subscription 으로

Owner transfer policy

Guild owner 가 다른 User 로 이양되면:

  • 기존 구독은 그대로 유지 (이전 owner 가 결제 지속)
  • 봇이 길드에서 강퇴되거나 길드가 삭제되면 자동 suspend

이 정책은 이전 owner 의 자발적 결제를 강제 중단하지 않기 위함이다.

Billing key ownership

빌링키는 항상 User 에 귀속된다.

  • 한 User 는 여러 빌링키를 가질 수 있음
  • Subscription 은 User 의 빌링키 중 하나를 참조
  • 빌링키 삭제 시 연결된 모든 active Subscription 은 suspend

Alternatives considered

Alternative 1: Guild = Tenant (단순 모델)

Pros

  • 개념 단순 (각 Guild 가 독립 고객)
  • License 와 Subscription 을 하나로 통합 가능

Cons

  • 같은 사람이 여러 Guild 운영 시 결제 수단 재등록 반복
  • 대시보드에서 "내 길드 전체 보기" 같은 뷰가 어색
  • Power user UX 열세

Why rejected — 여러 길드 운영이 흔한 현실에 부적합. Hybrid 의 추가 복잡도는 감수할 가치가 있음.

Alternative 2: User = Tenant (개인 구독, 모든 길드 적용)

Pros

  • 단일 구독으로 모든 길드에 혜택

Cons

  • 무임승차 — 한 Pro User 가 수십 개 길드에 혜택 전파 가능
  • 혜택 적용 대상 불명 (User 가 잠깐 들어간 길드도?)
  • 비즈니스적으로 수익 위협

Why rejected — 수익 모델 붕괴.

Alternative 3: Pooled subscription (여러 User 공동 결제)

Pros

  • 길드 멤버들이 n분의 1 결제

Cons

  • 구현 복잡도 (정산, 탈퇴 처리)
  • 결제 사고 위험
  • Discord 봇 SaaS 에서 흔한 패턴 아님

Why rejected — MVP 에 과도한 복잡도. 향후 Enterprise 급에서 검토 가능.

Compliance

  • Subscription entity 는 payer_user_idguild_id 둘 다 포함
  • 한 Guild 의 active subscription 유일성은 DB unique index 로 강제
  • 코드 리뷰에서 "subscription = user 1:1" 같은 가정 차단
  • UI 에서 결제 주체와 적용 대상을 명확히 구분 표시

Revisit triggers

  • Pooled 결제 요구가 Enterprise 고객에서 반복 발생하면 별도 ADR
  • 국가별 세금 처리가 복잡해지면(사업자 결제 등) 모델 확장

References

  • ADR-0012 — License 와 Subscription 분리
  • ADR-0013 — Toss Billing (결제 주체 = User)