OPEN HYPER STEP
← 목록으로 (stack-analysis)
STACK-ANALYSIS · 61 / 90
stack-analysis
CHAPTER 61 / 90
읽기 약 2
FUNCTION

SaaS 아키텍처: 멀티테넌시 설계


핵심 개념

Single DB·Schema per tenant·DB per tenant — 격리·비용·확장성 trade-off.

본문

멀티테넌시 3가지 모델

📋 코드 (15줄)
1. Single DB + tenant_id 컬럼 (가장 흔함)
   ✅ 비용 최저
   ✅ 운영 단순
   ❌ 격리 약함 (RLS로 보완)

2. Schema per tenant (PostgreSQL)
   ✅ 중간 격리
   ✅ 백업 분리 가능
   ❌ 마이그레이션 복잡

3. DB per tenant
   ✅ 완전 격리
   ✅ 컴플라이언스 (HIPAA·금융)
   ❌ 비용 N배
   ❌ 운영 복잡

Single DB + tenant_id (권장)

SQL📋 코드 (32줄)
-- 모든 테이블에 tenant_id
CREATE TABLE tenants (
  id        UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name      VARCHAR NOT NULL,
  slug      VARCHAR UNIQUE NOT NULL,
  plan      VARCHAR NOT NULL DEFAULT 'free',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE users (
  id         UUID PRIMARY KEY,
  tenant_id  UUID NOT NULL REFERENCES tenants(id),
  email      VARCHAR NOT NULL,
  role       VARCHAR NOT NULL DEFAULT 'member',
  UNIQUE(tenant_id, email)
);

CREATE TABLE projects (
  id         UUID PRIMARY KEY,
  tenant_id  UUID NOT NULL REFERENCES tenants(id),
  name       VARCHAR NOT NULL,
  -- 매 쿼리에 tenant_id 필터
  INDEX (tenant_id)
);


-- RLS (Row Level Security)
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

CREATE POLICY "tenant_isolation"
  ON projects FOR ALL
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

tenant 컨텍스트 미들웨어

TYPESCRIPT📋 코드 (25줄)
// middleware/tenant.ts
export async function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
  // subdomain 또는 path에서 추출
  const subdomain = req.hostname.split('.')[0];
  // app.acme.com → 'acme'

  const tenant = await db.tenant.findUnique({ where: { slug: subdomain } });
  if (!tenant) return res.status(404).json({ error: 'tenant_not_found' });

  // 사용자가 이 tenant의 멤버인가?
  if (req.user) {
    const member = await db.tenantMember.findFirst({
      where: { userId: req.user.id, tenantId: tenant.id },
    });
    if (!member) return res.status(403).json({ error: 'not_a_member' });
    req.tenantRole = member.role;
  }

  req.tenant = tenant;

  // PostgreSQL session variable로 RLS 적용
  await db.$executeRaw`SELECT set_config('app.tenant_id', ${tenant.id}, true)`;

  next();
}

Prisma 미들웨어로 자동 필터

TYPESCRIPT📋 코드 (18줄)
prisma.$use(async (params, next) => {
  const ctx = AsyncLocalStorage.getStore();
  if (!ctx?.tenantId) return next(params);

  // SELECT/UPDATE/DELETE에 tenant_id 자동 추가
  if (params.action === 'findMany' || params.action === 'findFirst') {
    params.args.where = {
      ...params.args.where,
      tenantId: ctx.tenantId,
    };
  }

  if (params.action === 'create') {
    params.args.data.tenantId = ctx.tenantId;
  }

  return next(params);
});

도메인 매핑

📋 코드 (13줄)
패턴:
- subdomain: acme.example.com
- path: example.com/acme
- custom domain: app.acme.com → CNAME → 우리 서버


[Custom Domain 처리]
1. 사용자가 app.acme.com 입력
2. CNAME → our-domain.com
3. Vercel/Cloudflare가 SSL 자동
4. Host 헤더로 tenant 매핑

DNS 레코드 자동 검증 — verify TXT

플랜·한도 관리

TYPESCRIPT📋 코드 (27줄)
const PLAN_LIMITS = {
  free:       { users: 3, projects: 5, storage: 1 },
  pro:        { users: 10, projects: 50, storage: 50 },
  business:   { users: 50, projects: 500, storage: 500 },
  enterprise: { users: -1, projects: -1, storage: -1 },  // 무제한
};


async function checkLimit(tenantId: string, resource: keyof typeof PLAN_LIMITS.free) {
  const tenant = await db.tenant.findUnique({ where: { id: tenantId } });
  const limit = PLAN_LIMITS[tenant!.plan][resource];
  if (limit === -1) return true;

  const current = await getCurrent(tenantId, resource);
  return current < limit;
}


// 사용
router.post('/projects', tenantMiddleware, async (req, res) => {
  const ok = await checkLimit(req.tenant.id, 'projects');
  if (!ok) return res.status(402).json({
    error: 'limit_exceeded',
    upgradeUrl: `/upgrade?from=${req.tenant.plan}`,
  });
  // ...
});

다음 챕터

CH.62 "결제 연동: Stripe Subscription".


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 프로젝트의 SaaS 멀티테넌시 부분을 분석해서
실전 분석 + 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

SaaS 멀티테넌시 관련 실제 서비스 5개를
비교 분석해서 패턴 추출를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 코드베이스에서 SaaS 멀티테넌시
최적화 가능 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 풀스택 시장의
SaaS 멀티테넌시 트렌드를 솔직히 알려줘.

⭐ 이것만 기억하세요
SaaS 아키텍처: 멀티테넌시 설계 이 3가지만 확실히 잡으세요
1.멀티테넌시는 99% Single DB + tenant_id가 정답 — 비용·운영 효율
2.RLS + Prisma 미들웨어로 자동 격리 — 휴먼 에러 방지
3.플랜·한도 관리는 DB 레벨에서 — checkLimit 미들웨어


공유하기
진행도 61 / 90