LabHub

블로그

SaaS 아키텍처 완전 가이드 — Multi-tenancy·Billing·Feature Flag·Audit·RBAC·SSO를 2025년 기준으로 한 번에 정리

프롤로그 — "API를 SaaS로 팔려면?"

당신에게 훌륭한 API(Ep 19)가 있다. 이걸 SaaS(Software as a Service)로 팔려면?

"그냥 signup 페이지 만들고 Stripe 연결하면 되죠?" — 2018년이라면. 2025년엔 더 어려워졌다:

좋은 SaaS는 차별화된 기능이 아니라 신뢰할 수 있는 플랫폼에서 시작한다.

이 글은 Season 2 Ep 20 — SaaS 아키텍처. Multi-tenant 전략, Billing, Feature Flag, Audit, RBAC/ABAC, SSO, Usage Metering까지.


1부 — Multi-tenancy 3전략

1. Silo (격리) — DB per tenant

Tenant ADB-A
Tenant BDB-B
Tenant CDB-C

장점:

단점:

사용처: 금융, 의료, 정부. 고가 플랜.

2. Pool (공유) — 같은 DB, tenant_id로 구분

CREATE TABLE users (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  email TEXT,
  ...
);
CREATE INDEX idx_users_tenant ON users(tenant_id);

Row-Level Security (Postgres):

ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
  USING (tenant_id = current_setting('app.current_tenant')::UUID);

장점:

단점:

3. Bridge (하이브리드) — 플랜별 구분

Free, Starter: Pool DB
Pro: Pool DB + 격리된 schema
Enterprise: Silo (dedicated DB)

현실의 표준. 성장 단계별 다른 모델.

Schema-per-tenant (Postgres 특화)

CREATE SCHEMA tenant_abc;
CREATE TABLE tenant_abc.users (...);

-- connection string: search_path=tenant_abc,public

장점: 코드는 Pool, 격리는 Silo 중간 단점: Postgres는 스키마 1000+개부터 느려짐

테넌트 식별 전략

방법 1: Subdomain (acme.example.com)
방법 2: Path prefix (/t/acme/...)
방법 3: Header (X-Tenant-ID)
방법 4: JWT claim

2025 권장: Subdomain + JWT claim (안전 2중 확인).

Middleware 패턴

// Express middleware
async function tenantMiddleware(req, res, next) {
  const subdomain = req.hostname.split('.')[0];
  const tenant = await db.tenant.findFirst({ where: { slug: subdomain } });
  if (!tenant) return res.status(404).json({ error: 'Tenant not found' });

  req.tenant = tenant;
  // Row-Level Security용 세션 변수
  await db.$executeRaw`SET LOCAL app.current_tenant = ${tenant.id}`;
  next();
}

2부 — Billing 시스템 설계

과금 모델 5가지

모델예시특징
Flat-rateBasecamp단순, seat 무제한
Per-seatSlack, Notion사용자 수
TieredStripe, Twilio사용량 구간별
Usage-basedAWS, OpenAIAPI call당, 저장량
Hybrid기본 + 초과분현실의 표준

도구 비교

도구특징
Stripe Billing구독 + usage, 세금 자동(Stripe Tax), 사실상 표준
Lago (오픈소스)Event-driven, self-host, 복잡한 pricing 유연
Metronome복잡 usage-based SaaS 전용
PaddleMerchant of Record, 세금/환불 대행
Chargebee엔터프라이즈, 복잡한 workflow
RevenueCat모바일 앱 구독 전용
OrbUsage-based 2024 신흥

Stripe Billing 모델

Product — 논리적 상품 ("Pro Plan")
  └── Price — 실제 가격 ("Pro Monthly $10")
        └── Subscription — 고객의 구독
              └── Invoice — 월마다 생성
                    └── Charge — 실제 결제

Usage-based 구현

// 사용자가 API 호출할 때
await stripe.subscriptionItems.createUsageRecord(
  subscriptionItem.id,
  {
    quantity: 1,  // 또는 배치 처리 후 총합
    timestamp: Math.floor(Date.now() / 1000),
    action: 'increment',
  }
);

2025 best practice: Stripe의 usage를 실시간 X, 배치(시간당/일일)로 집계 후 전송. 이벤트 로그 자체 보관.

흔한 함정

  1. Upgrade/Downgrade prorated — 계산 복잡, Stripe 자동 OK
  2. 무료 trial → paid 전환 — 실패 처리 (카드 만료, 잔액 부족)
  3. Tax 처리 — 한국 부가세, EU VAT, 미국 주별 세금
  4. Dunning (연체 추심) — 실패 시 3-7-14일 재시도
  5. Refund + partial refund
  6. Chargeback (사용자가 카드사에 이의 제기)

Revenue Recognition (매출 인식)

$120/연 일시불 → 매출 인식은 매월 $10씩 12개월
이유: 서비스 제공 시점에 매출 인식 (GAAP/IFRS)
도구: Stripe Revenue Recognition, Sage Intacct

3부 — Feature Flag

왜 필요한가

배포 = 릴리즈 분리
- 배포는 코드가 서버에 가는 것
- 릴리즈는 사용자에게 보이는 것
- Feature Flag가 둘을 분리

혜택:

도구 비교

도구특징
LaunchDarkly시장 선도, 비쌈
Unleash (오픈소스)Self-host, 기업 친화
FlagsmithSelf-host + Cloud
PostHogFeature flag + Analytics 통합
StatsigA/B 테스트 강력
ConfigCat단순, 저렴
OpenFeatureCNCF 표준 인터페이스

OpenFeature — 벤더 중립 표준

import { OpenFeature } from '@openfeature/server-sdk';

const client = OpenFeature.getClient();
const showNewFeature = await client.getBooleanValue('new-feature', false, {
  targetingKey: user.id,
  email: user.email,
  plan: user.plan,
});

장점: LaunchDarkly ↔ Unleash ↔ PostHog 교체 시 코드 변경 최소.

규칙 예시

flag: new-checkout-flow
defaultValue: false
rules:
  - if: plan == "enterprise"
    then: true
  - if: email endsWith "@example.com"
    then: true
  - percentage: 10  # 10%
    then: true

안티패턴

  1. Flag debt — 오래된 플래그 수천 개 → 분기 지옥
  2. Flag 안에 또 flag → 테스트 불가
  3. Kill switch만 쓰고 제거 안 함 → 영구 코드
  4. 매 요청 flag 서비스 호출 → 지연 폭발 (SDK는 캐싱 필수)

Cleanup 전략

Flag 생성 시 deprecation date 설정
30일 후 자동 알림
90일 후 강제 제거 issue 생성

4부 — Audit Log 설계

왜 필요한가

이벤트 포맷 (CloudEvents 호환)

{
  "id": "aud_01HX...",
  "specversion": "1.0",
  "type": "user.permission.granted",
  "source": "/api/users/123",
  "time": "2026-04-15T12:34:56Z",
  "subject": "usr_abc",
  "data": {
    "actor": { "type": "user", "id": "usr_admin", "email": "admin@example.com" },
    "target": { "type": "user", "id": "usr_abc" },
    "action": "permission.granted",
    "before": { "role": "viewer" },
    "after": { "role": "admin" },
    "ip": "1.2.3.4",
    "userAgent": "...",
    "requestId": "req_xyz"
  }
}

저장소 선택

옵션 1: Postgres (간단한 경우)
  + ACID, 기존 DB
  - 스케일 한계

옵션 2: ClickHouse / BigQuery (대규모)
  + 빠른 분석
  - 별도 시스템

옵션 3: S3 + Athena (아카이브)
  + 매우 저렴
  - 쿼리 느림

옵션 4: 전용 SaaS (Vanta, OneSchema)

2025 패턴: Primary는 Postgres, 30일 이후 S3 아카이브, ClickHouse로 분석.

위변조 방지

쿼리 UX

고객이 볼 수 있는 Audit Log UI:

민감정보 처리


5부 — RBAC vs ABAC vs PBAC

RBAC (Role-Based) — 간단

Role: Admin, Editor, Viewer
Permission: read/write/delete + resource

Admin → 모든 권한
Editor → read/write
Viewer → read only

장점: 단순, 이해 쉬움 단점: 세밀한 권한 표현 어려움 ("A 프로젝트만 편집")

ABAC (Attribute-Based) — 유연

rule: allow if
  user.role == "editor" AND
  resource.owner == user.id AND
  environment.ip.country == "KR"

도구:

Cedar 예시

permit(
  principal == User::"alice",
  action == Action::"read",
  resource
) when {
  resource.owner == principal ||
  resource.sharedWith.contains(principal)
};

RBAC 구현 (Postgres)

CREATE TABLE roles (id UUID PRIMARY KEY, name TEXT);
CREATE TABLE permissions (id UUID PRIMARY KEY, action TEXT, resource TEXT);
CREATE TABLE role_permissions (role_id UUID, permission_id UUID, PRIMARY KEY (role_id, permission_id));
CREATE TABLE user_roles (user_id UUID, role_id UUID, PRIMARY KEY (user_id, role_id));

실전 조합

Core RBAC (Admin/Editor/Viewer)
+ Resource-level permissions (Project membership)
+ Attribute rules (Time, IP, MFA)

2025 도구


6부 — SSO (Single Sign-On)

왜 엔터프라이즈가 요구

프로토콜

프로토콜연식특징
SAML 2.02005엔터프라이즈 표준, XML
OIDC (OpenID Connect)2014OAuth 2.0 기반, JSON, 현대적
OAuth 2.02012인증 아닌 인가 (자주 혼동)
SCIM2015사용자 프로비저닝 (직원 입/퇴사)

SAML 기본 흐름

1. 사용자 → SP (Service Provider, 우리 SaaS)
2. SPIdP (Identity Provider, Okta)로 redirect
3. IdP: 사용자 인증 (MFA)
4. IdP → 브라우저 → SPSAML Response 전달
5. SP: 서명 검증 → 세션 생성

OIDC 기본 흐름

Authorization Code + PKCE:
1. Client/authorize (IdP)
2. 사용자 로그인
3. IdPClient로 code 반환
4. Client/token으로 code 교환 → access_token + id_token(JWT)

구현 방법

옵션 1: 직접 구현

옵션 2: SSO SaaS (2025 권장)

장점: 수백 개 IdP 통합 지원, IT 관리자 셀프 서비스 UI.

SCIM — 자동 프로비저닝

회사 A가 직원 50명 입사:
OktaSCIM → 우리 SaaS에 자동 생성

직원 퇴사 시:
OktaSCIM → 계정 비활성화

수동 관리 안 해도  (엔터프라이즈 필수)

7부 — Usage Metering

이벤트 수집 파이프라인

AppKafka/KinesisAggregatorBilling engine → Invoice
                            Analytics DB (ClickHouse)

설계 원칙

  1. 이벤트는 불변 (절대 수정 X)
  2. 멱등성: 같은 event_id 두 번 받아도 1번 계산
  3. Idempotency 윈도우: 중복 제거 7일까지
  4. Ordered? No: 분산 시스템에서 순서 기대 X
  5. Granularity: 원본 이벤트 보관 (집계만 보관하면 회귀 불가)

Aggregation 예시

-- 시간별 집계 (ClickHouse)
SELECT
  tenant_id,
  toStartOfHour(timestamp) as hour,
  count() as api_calls,
  sum(bytes) as bytes_total
FROM events
WHERE timestamp >= today()
GROUP BY tenant_id, hour;

Lago / Metronome

Lago (오픈소스):
- Event ingestion API
- Aggregation (count, sum, max, unique)
- Pricing rules (tier, package, graduated)
- Invoice 자동 생성
- Stripe 연동

Metronome (상용):
- 더 복잡한 enterprise pricing
- Commitment, Credits 모델

8부 — Customer Data Isolation

Tenant-level Encryption

각 tenant마다 고유 암호화 키
Master KeyTenant Key (envelope encryption)
Tenant Key로 데이터 암호화

장점: 규정 (HIPAA, PCI) 만족
도구: AWS KMS, HashiCorp Vault

BYOK (Bring Your Own Key)

엔터프라이즈 고객이 자신의 KMS 키 가져옴 → 해지 시 데이터 사용 불가.

Data Residency

미국 고객 → US region
EU 고객 → EU region (GDPR)
한국 고객 → AP region (금융 규정)

구현: tenant 레코드에 region 저장 → 라우팅
복잡: cross-region 백업, 장애 조치

9부 — SOC 2, ISO 27001, GDPR

SOC 2 (미국 엔터프라이즈 필수)

Type I: 설계 감사 (한 시점)
Type II: 운영 감사 (6-12개월)

5 Trust Services Criteria:
- Security (필수)
- Availability
- Processing Integrity
- Confidentiality
- Privacy

핵심 요구:

도구

GDPR (EU)

코드에 녹여놓기

// 사용자 삭제
async function deleteUser(userId: string) {
  await db.$transaction(async (tx) => {
    await tx.user.update({
      where: { id: userId },
      data: {
        email: `deleted-${userId}@example.com`,
        name: 'Deleted User',
        phoneNumber: null,
        deletedAt: new Date(),
      },
    });
    await tx.session.deleteMany({ where: { userId } });
    await tx.auditLog.create({
      data: { action: 'user.deleted', userId, ... },
    });
  });
  // 비동기: 백업, S3, 외부 시스템 삭제
  await jobs.enqueue('gdpr.delete', { userId });
}

10부 — Onboarding과 Activation

Time-to-Value (TTV) 최소화

좋은 SaaS의 목표: 회원가입 → 첫 "와우" 순간까지 5분 이내

나쁜 예:
1. 회원가입 → 이메일 인증 (5)
2. 긴 설정 위저드 (10)
3. 샘플 데이터 직접 추가 (20)

좋은 예:
1. Google OAuth (10)
2. 샘플 프로젝트 자동 생성
3. 즉시 "와우" 경험

체크리스트 패턴

"완료 진행 4/10"
- [x] 프로필 작성
- [x] 팀 초대
- [ ] 첫 프로젝트 만들기
- [ ] Integration 연결
...

Product Tour

Empty States

Before: "No data" (나쁨)
After: "첫 프로젝트를 만들어 보세요" + 가이드 + 샘플 템플릿

11부 — Analytics와 Observability

3가지 Analytics

  1. Product Analytics — Mixpanel, Amplitude, PostHog
  2. Marketing Analytics — GA4, Plausible (privacy)
  3. Business Metrics — MRR, Churn, LTV (Baremetrics, ChartMogul)

SaaS KPI 10개

지표설명
MRR (Monthly Recurring Revenue)예측 가능한 월 매출
ARR연 매출 (MRR × 12)
Churn이탈률 (월 5% = 위험)
Net Revenue Retention (NRR)기존 고객 매출 유지/확장
CAC (Customer Acquisition Cost)고객 획득 비용
LTV (Lifetime Value)고객 생애 가치
LTV:CAC3:1 이상 권장
Payback PeriodCAC 회수 기간
Activation Rate가입 → 핵심 행동 %
DAU/MAU참여도

PostHog — 오픈소스 올인원

import posthog from 'posthog-js';

posthog.init('<api_key>', { api_host: 'https://app.posthog.com' });

posthog.identify(user.id, { email: user.email, plan: user.plan });
posthog.capture('project_created', { project_id: '...', template: 'blank' });

기능: Events, Feature Flag, Session Replay, A/B test, Heatmap, Funnels.


12부 — 운영 체크리스트

Day-one SaaS Checklist

Day-1K Customers Checklist


13부 — 6개월 로드맵

1개월차: Multi-tenant 모델 결정, RLS(Postgres) 적용, 기본 인증 2개월차: Stripe Billing, Webhook, Subscription lifecycle 3개월차: Feature Flag (OpenFeature), Audit Log 기본 4개월차: SSO (OIDC + SAML), WorkOS 연동, SCIM 기본 5개월차: RBAC + ABAC(OPA), tenant-level encryption 6개월차: SOC 2 준비 (Vanta), Data export, GDPR 대응


14부 — 체크리스트 12개


15부 — 안티패턴 10가지

  1. Multi-tenancy를 나중에 결정 → 재구조화 지옥
  2. Billing을 직접 구현 → 세금, 환불, prorated에서 자멸
  3. Feature Flag 없이 배포 → Kill switch 없는 장애
  4. Audit Log 없음 → 엔터프라이즈 포기
  5. RBAC 없이 if문 분기 → 권한 지옥
  6. SSO 안 지원 → 엔터프라이즈 영업 실패
  7. Free plan에 제한 없음 → 악용 + 비용 폭탄
  8. Data export 없음 → 고객 락인 불만
  9. Rate limit 없음 → API 남용 + DB 부하
  10. SOC 2 "나중에" → 큰 계약 놓침

마무리 — "SaaS는 신뢰 게임"

2025년 SaaS의 본질은 신뢰:

"기능 더 많이"가 아니라 "덜 망가지고, 안전하게, 예측 가능하게"가 엔터프라이즈 판매의 본질.

Season 2의 기술 딥다이브는 여기까지. 다음은 기술을 넘어선 것들.

다음 글은 Season 2 Ep 21 — 엔지니어링 매니지먼트 완전 가이드. 1:1, 피드백, Hiring, Performance Review, Tech Debt 협상, IC → Manager 전환까지. 시스템을 만들었다면, 시스템을 만드는 사람들을 이끄는 법.


다음 글 예고 — "엔지니어링 매니지먼트 완전 가이드: 1:1·피드백·Hiring·Tech Debt·Career Ladder"

Season 2 Ep 21은:

코드보다 사람이 더 복잡하다. 다음 글에서.

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다