Use Cases

Implementing Multi-Tenant Architecture: Claude Code 활용 가이드

implementing multi-tenant architecture: Claude Code 활용. 실용적인 코드 예시를 포함합니다.

マルチテナント설계をClaude Code로 구현하기

SaaS애플리케이션ではマルチテナント설계が필수적입니다。テナント間の데이터分離、テナント固有の설정관리、アクセス制御をどう実現するかが重要な설계判断となります。Claude Code를 활용하면 적절한マルチテナントパターンを一貫性のあるコードで구현할 수 있습니다。

テナント分離パターンの선택

主なアプローチは3つあります。

  1. 行レベル分離: 全テナントが同じ테이블を共有し、tenantIdカラムでフィルタリング
  2. 스키마分離: テナントごとに別のDB스키마を使用
  3. DB分離: テナントごとに独立した데이터베이스

Claude Code에설계判断を相談することも할 수 있습니다。

> SaaS앱のマルチテナント설계에 대해相談したい。
> 想定テナント数は1000程度、각テナントの데이터量は中程度。
> 行レベル分離で구현して、テナントID에 의한フィルタリングを
> 미들웨어で자동화して。

テナント解決미들웨어

// src/middleware/tenant.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';

export async function resolveTenant(request: NextRequest) {
  // サブドメインからテナントを解決
  const hostname = request.headers.get('host') || '';
  const subdomain = hostname.split('.')[0];

  // カスタムドメイン의 경우
  const tenant = await prisma.tenant.findFirst({
    where: {
      OR: [
        { subdomain },
        { customDomain: hostname },
      ],
      isActive: true,
    },
  });

  if (!tenant) {
    return NextResponse.json({ error: 'テナントが見つかりません' }, { status: 404 });
  }

  // 요청헤더にテナントIDを注入
  const headers = new Headers(request.headers);
  headers.set('x-tenant-id', tenant.id);

  return NextResponse.next({ request: { headers } });
}

テナントスコープ付きPrisma클라이언트

// src/lib/tenant-prisma.ts
import { PrismaClient } from '@prisma/client';

export function createTenantPrisma(tenantId: string) {
  const prisma = new PrismaClient().$extends({
    query: {
      $allModels: {
        async findMany({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async findFirst({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async create({ args, query }) {
          args.data = { ...args.data, tenantId };
          return query(args);
        },
        async update({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async delete({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
      },
    },
  });

  return prisma;
}

テナント설정の관리

// src/services/tenant-settings.ts
interface TenantSettings {
  branding: {
    primaryColor: string;
    logo?: string;
    companyName: string;
  };
  features: {
    maxUsers: number;
    storageLimit: number;
    apiAccess: boolean;
  };
  notifications: {
    emailEnabled: boolean;
    slackWebhook?: string;
  };
}

export class TenantSettingsService {
  async getSettings(tenantId: string): Promise<TenantSettings> {
    const tenant = await prisma.tenant.findUnique({
      where: { id: tenantId },
      select: { settings: true, plan: true },
    });

    // プラン에 기반한デフォルト설정とカスタム설정をマージ
    const planDefaults = getPlanDefaults(tenant!.plan);
    return { ...planDefaults, ...tenant!.settings } as TenantSettings;
  }

  async updateSettings(tenantId: string, updates: Partial<TenantSettings>) {
    return prisma.tenant.update({
      where: { id: tenantId },
      data: { settings: updates },
    });
  }
}

데이터分離테스트の重要性

マルチテナント구현では、テナント間の데이터漏洩を防ぐ테스트が非常に중요합니다。Claude Code에「テナントAの데이터がテナントBからアクセスできないことを확인する테스트」を依頼して、十分な커버리지を確保합시다。

関連リソース

ロールベースのアクセス制御はRBAC구현가이드、데이터베이스설계は데이터베이스마이그레이션를 참고하세요.

PostgreSQLの行レベル보안(RLS)에 대해서는PostgreSQL공식 문서(postgresql.org/docs)를 확인하세요.

#Claude Code #multi-tenant #SaaS #PostgreSQL #security