✍️ 필사 모드: SaaS 아키텍처 완전 가이드 — Multi-tenancy·Billing·Feature Flag·Audit·RBAC·SSO를 2025년 기준으로 한 번에 정리
한국어프롤로그 — "API를 SaaS로 팔려면?"
당신에게 훌륭한 API(Ep 19)가 있다. 이걸 **SaaS(Software as a Service)**로 팔려면?
"그냥 signup 페이지 만들고 Stripe 연결하면 되죠?" — 2018년이라면. 2025년엔 더 어려워졌다:
- Multi-tenancy: 고객 데이터 격리는 기본값
- Billing: 월 구독, 사용량, 연간 할인, prorated 변경
- Feature Flag: 일부 고객만 새 기능
- Audit Log: 엔터프라이즈가 요구
- RBAC/SSO: Admin/Viewer 권한, SAML 로그인
- SOC 2: 보안 감사 준수
- Usage Metering: API call당 과금
- Data Export: 고객이 언제든 자기 데이터 가져가야
좋은 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 A → DB-A
Tenant B → DB-B
Tenant C → DB-C
장점:
- 강한 격리
- Tenant별 성능/보안/규정 분리
- "한 고객 데이터가 다른 고객에게 유출" 불가능
단점:
- 비용 (DB 수백 개)
- 마이그레이션 지옥 (스키마 변경 시 모든 DB)
- 엔터프라이즈 단독 고객 적합
사용처: 금융, 의료, 정부. 고가 플랜.
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);
장점:
- 비용 효율
- 마이그레이션 쉬움
- 스타트업 친화
단점:
- 격리 약함 (코드 버그 시 cross-tenant 유출 위험)
- Noisy neighbor (한 tenant가 DB 독점)
- 규정 어려움
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-rate | Basecamp | 단순, seat 무제한 |
| Per-seat | Slack, Notion | 사용자 수 |
| Tiered | Stripe, Twilio | 사용량 구간별 |
| Usage-based | AWS, OpenAI | API call당, 저장량 |
| Hybrid | 기본 + 초과분 | 현실의 표준 |
도구 비교
| 도구 | 특징 |
|---|---|
| Stripe Billing | 구독 + usage, 세금 자동(Stripe Tax), 사실상 표준 |
| Lago (오픈소스) | Event-driven, self-host, 복잡한 pricing 유연 |
| Metronome | 복잡 usage-based SaaS 전용 |
| Paddle | Merchant of Record, 세금/환불 대행 |
| Chargebee | 엔터프라이즈, 복잡한 workflow |
| RevenueCat | 모바일 앱 구독 전용 |
| Orb | Usage-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, **배치(시간당/일일)**로 집계 후 전송. 이벤트 로그 자체 보관.
흔한 함정
- Upgrade/Downgrade prorated — 계산 복잡, Stripe 자동 OK
- 무료 trial → paid 전환 — 실패 처리 (카드 만료, 잔액 부족)
- Tax 처리 — 한국 부가세, EU VAT, 미국 주별 세금
- Dunning (연체 추심) — 실패 시 3-7-14일 재시도
- Refund + partial refund
- Chargeback (사용자가 카드사에 이의 제기)
Revenue Recognition (매출 인식)
$120/연 일시불 → 매출 인식은 매월 $10씩 12개월
이유: 서비스 제공 시점에 매출 인식 (GAAP/IFRS)
도구: Stripe Revenue Recognition, Sage Intacct
3부 — Feature Flag
왜 필요한가
배포 = 릴리즈 분리
- 배포는 코드가 서버에 가는 것
- 릴리즈는 사용자에게 보이는 것
- Feature Flag가 둘을 분리
혜택:
- Canary: 1% → 10% → 50% → 100%
- Kill switch: 장애 시 즉시 끄기
- A/B 테스트: 버전 A vs B 비교
- Premium feature: 플랜별 기능 다르게
- Dark launch: 내부만 먼저
도구 비교
| 도구 | 특징 |
|---|---|
| LaunchDarkly | 시장 선도, 비쌈 |
| Unleash (오픈소스) | Self-host, 기업 친화 |
| Flagsmith | Self-host + Cloud |
| PostHog | Feature flag + Analytics 통합 |
| Statsig | A/B 테스트 강력 |
| ConfigCat | 단순, 저렴 |
| OpenFeature | CNCF 표준 인터페이스 |
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
안티패턴
- Flag debt — 오래된 플래그 수천 개 → 분기 지옥
- Flag 안에 또 flag → 테스트 불가
- Kill switch만 쓰고 제거 안 함 → 영구 코드
- 매 요청 flag 서비스 호출 → 지연 폭발 (SDK는 캐싱 필수)
Cleanup 전략
Flag 생성 시 deprecation date 설정
30일 후 자동 알림
90일 후 강제 제거 issue 생성
4부 — Audit Log 설계
왜 필요한가
- SOC 2, ISO 27001, GDPR 요구
- 보안 사고 포렌식
- 내부 조사 (누가 뭘 언제 했나)
- 고객 요구 (엔터프라이즈 필수)
이벤트 포맷 (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로 분석.
위변조 방지
- Append-only: UPDATE/DELETE 금지, 트리거로 강제
- Hash chain: 블록체인 아이디어 (각 레코드가 이전 hash 포함)
- Database-level audit: pgaudit, AWS RDS audit
- WORM storage: Write Once Read Many (S3 Object Lock)
쿼리 UX
고객이 볼 수 있는 Audit Log UI:
- Filter: actor, action, date range, resource
- Export: CSV, JSON
- Webhook: 특정 이벤트 발생 시 알림
민감정보 처리
- 비밀번호, 토큰은 절대 기록 X
- PII는 해시화 또는 마스킹
- GDPR: 사용자 삭제 시 audit log는 보존 의무와 충돌 → 법무 확인
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"
도구:
- OPA (Open Policy Agent) + Rego
- Cedar (AWS, 2023)
- Casbin
- Oso
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 도구
- Permit.io, Oso, Cerbos — 권한 SaaS
- Clerk, Kinde, WorkOS — 인증 + 권한 SaaS
6부 — SSO (Single Sign-On)
왜 엔터프라이즈가 요구
- IT 관리: 직원 입/퇴사 시 한 곳(Okta, Entra)에서 관리
- 보안: MFA 한 번, 모든 앱 공통
- 감사: 중앙 로그
프로토콜
| 프로토콜 | 연식 | 특징 |
|---|---|---|
| SAML 2.0 | 2005 | 엔터프라이즈 표준, XML |
| OIDC (OpenID Connect) | 2014 | OAuth 2.0 기반, JSON, 현대적 |
| OAuth 2.0 | 2012 | 인증 아닌 인가 (자주 혼동) |
| SCIM | 2015 | 사용자 프로비저닝 (직원 입/퇴사) |
SAML 기본 흐름
1. 사용자 → SP (Service Provider, 우리 SaaS)
2. SP → IdP (Identity Provider, Okta)로 redirect
3. IdP: 사용자 인증 (MFA)
4. IdP → 브라우저 → SP로 SAML Response 전달
5. SP: 서명 검증 → 세션 생성
OIDC 기본 흐름
Authorization Code + PKCE:
1. Client → /authorize (IdP)
2. 사용자 로그인
3. IdP → Client로 code 반환
4. Client → /token으로 code 교환 → access_token + id_token(JWT)
구현 방법
옵션 1: 직접 구현
passport-saml,openid-client- 장점: 통제
- 단점: 복잡, 고객별 설정 지옥
옵션 2: SSO SaaS (2025 권장)
- WorkOS: B2B SSO 전문
- Clerk: SSO + 인증 전반
- Auth0 (Okta): 엔터프라이즈
- Supabase Auth, Firebase Auth: 스타트업
장점: 수백 개 IdP 통합 지원, IT 관리자 셀프 서비스 UI.
SCIM — 자동 프로비저닝
회사 A가 직원 50명 입사:
Okta → SCIM → 우리 SaaS에 자동 생성
직원 퇴사 시:
Okta → SCIM → 계정 비활성화
수동 관리 안 해도 됨 (엔터프라이즈 필수)
7부 — Usage Metering
이벤트 수집 파이프라인
App → Kafka/Kinesis → Aggregator → Billing engine → Invoice
↓
Analytics DB (ClickHouse)
설계 원칙
- 이벤트는 불변 (절대 수정 X)
- 멱등성: 같은 event_id 두 번 받아도 1번 계산
- Idempotency 윈도우: 중복 제거 7일까지
- Ordered? No: 분산 시스템에서 순서 기대 X
- 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 Key → Tenant 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
핵심 요구:
- 접근 제어 (MFA, 최소 권한)
- 변경 관리 (Code review, CI)
- 침입 탐지 (SIEM)
- Audit log
- 백업 + DR
- Vendor management
도구
- Vanta, Drata, Secureframe: SOC 2 자동화 ($20K-50K/년)
- Vanta AI Questionnaire: 보안 설문 자동 응답
GDPR (EU)
- Data export (right to access)
- Data deletion (right to be forgotten)
- Consent (cookie banner)
- DPA (Data Processing Agreement)
- Breach notification (72시간 이내)
코드에 녹여놓기
// 사용자 삭제
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
- Intro.js, Shepherd.js — 오픈소스
- Pendo, Userflow, Chameleon — SaaS
Empty States
Before: "No data" (나쁨)
After: "첫 프로젝트를 만들어 보세요" + 가이드 + 샘플 템플릿
11부 — Analytics와 Observability
3가지 Analytics
- Product Analytics — Mixpanel, Amplitude, PostHog
- Marketing Analytics — GA4, Plausible (privacy)
- 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:CAC | 3:1 이상 권장 |
| Payback Period | CAC 회수 기간 |
| 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
- Multi-tenant 모델 결정 (Pool/Silo/Bridge)
- Stripe Billing + Webhook
- SSO 준비 (OIDC + SAML 지원)
- Audit Log 인프라
- Feature Flag (OpenFeature + 벤더)
- Rate Limiting per tenant
- Email (Resend, Postmark) + template
- Support chat (Intercom, Crisp, Front)
- Docs (Mintlify, Docusaurus)
- Status page (Atlassian Statuspage, Better Stack)
- Uptime monitoring (Pingdom, Better Uptime)
- Error tracking (Sentry)
- Analytics (PostHog)
- SOC 2 준비 (Vanta)
Day-1K Customers Checklist
- 다중 리전 배포
- Dedicated enterprise 인프라
- BYOK (Bring Your Own Key)
- SCIM 프로비저닝
- IP allowlist per tenant
- Data export API
- 24/7 on-call rotation
- SLA 문서
- ISO 27001, SOC 2 Type II
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개
- Multi-tenant 데이터 격리 (RLS or 분리 DB)
- Stripe Billing + 재시도 + Dunning
- Feature Flag 도구 도입 (OpenFeature)
- Audit Log 수집 + 보관 정책
- RBAC + Resource-level permission
- SSO (OIDC 기본, SAML 엔터프라이즈)
- SCIM 프로비저닝
- Rate Limiting per tenant
- Data export (GDPR)
- SOC 2 준비 (Vanta)
- Status page + uptime monitor
- PostHog 또는 Mixpanel로 product analytics
15부 — 안티패턴 10가지
- Multi-tenancy를 나중에 결정 → 재구조화 지옥
- Billing을 직접 구현 → 세금, 환불, prorated에서 자멸
- Feature Flag 없이 배포 → Kill switch 없는 장애
- Audit Log 없음 → 엔터프라이즈 포기
- RBAC 없이 if문 분기 → 권한 지옥
- SSO 안 지원 → 엔터프라이즈 영업 실패
- Free plan에 제한 없음 → 악용 + 비용 폭탄
- Data export 없음 → 고객 락인 불만
- Rate limit 없음 → API 남용 + DB 부하
- SOC 2 "나중에" → 큰 계약 놓침
마무리 — "SaaS는 신뢰 게임"
2025년 SaaS의 본질은 신뢰:
- 데이터 격리 = 신뢰
- Audit Log = 신뢰
- SSO/SCIM = 기업 IT 신뢰
- SOC 2 = 감사 신뢰
- Status page = 투명성
- Feature Flag = 점진적 신뢰
"기능 더 많이"가 아니라 **"덜 망가지고, 안전하게, 예측 가능하게"**가 엔터프라이즈 판매의 본질.
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은:
- 1:1 미팅 베스트 프랙티스
- Feedback: SBI, Radical Candor
- Hiring: Scorecard, Interview Loop
- Performance Review + Calibration
- Tech Debt 협상 (Product와)
- IC vs Manager trade-offs
- Career Ladder (Junior → Staff → Principal)
- 팀 건강 (Retention, Burnout)
코드보다 사람이 더 복잡하다. 다음 글에서.
현재 단락 (1/452)
당신에게 훌륭한 API(Ep 19)가 있다. 이걸 **SaaS(Software as a Service)**로 팔려면?