인베스트리 투자조합 관리 ERP, Lotus Soft 창업사관학교 플랫폼, Athena 정보보호관리 시스템 — 세 프로젝트 모두 “여러 기업이 하나의 플랫폼을 각자의 공간으로 사용”하는 멀티테넌트 구조입니다. 실제 개발 경험으로 멀티테넌트 설계의 핵심을 정리합니다.
멀티테넌트(Multi-tenant)란?
하나의 소프트웨어 인스턴스를 여러 고객(테넌트)이 공유하는 구조입니다. 각 테넌트는 자신의 데이터만 보고 관리하며, 다른 테넌트의 데이터는 볼 수 없습니다.
대표적인 예: Slack, Notion, Salesforce. 같은 소프트웨어인데 삼성 직원은 삼성 워크스페이스만, LG 직원은 LG 워크스페이스만 보입니다.
멀티테넌트 DB 설계 3가지 방식
방식 1 — 데이터베이스 완전 분리
테넌트마다 별도의 데이터베이스를 생성합니다.
tenant_a_db
├── users
├── projects
└── invoices
tenant_b_db
├── users
├── projects
└── invoices
장점: 완벽한 데이터 격리, 테넌트별 독립적 백업/복원 가능, 성능 예측 가능
단점: 테넌트 수가 늘면 DB 수도 늘어 관리 복잡도 증가, 비용 높음
Athena처럼 정보보호 데이터를 다루거나 금융 데이터가 포함된 경우 이 방식이 적합합니다.
방식 2 — 스키마 분리
같은 DB, 테넌트별 스키마(네임스페이스)를 분리합니다. PostgreSQL에서 주로 사용합니다.
-- tenant_a 스키마
CREATE SCHEMA tenant_a;
CREATE TABLE tenant_a.users (...);
-- tenant_b 스키마
CREATE SCHEMA tenant_b;
CREATE TABLE tenant_b.users (...);
장점: DB는 하나, 논리적 분리 가능
단점: 스키마 수가 많아지면 마이그레이션 관리가 복잡
방식 3 — 행 수준 분리 (Row-Level Security)
같은 테이블에 tenant_id 컬럼으로 구분합니다.
CREATE TABLE users (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL, -- 테넌트 구분자
name VARCHAR(100),
email VARCHAR(255)
);
-- 조회 시 반드시 tenant_id 필터 적용
SELECT * FROM users WHERE tenant_id = 'tenant_a_uuid';
장점: 구조 단순, 비용 낮음, 테넌트 추가 쉬움
단점: 실수로 tenant_id 필터를 빠뜨리면 데이터 노출 위험
인베스트리, Lotus Soft처럼 중간 규모의 B2B SaaS에 적합합니다. 단, 모든 쿼리에 tenant_id 필터를 강제하는 미들웨어 레이어가 필수입니다.
권한 관리 (RBAC) 설계
멀티테넌트 SaaS에서 권한 관리는 두 축으로 나뉩니다.
축 1 — 플랫폼 레벨: 슈퍼 어드민(플랫폼 전체 관리), 테넌트 어드민(자사 관리), 일반 사용자
축 2 — 기능 레벨: 읽기, 쓰기, 삭제, 관리
Lotus Soft의 경우:
- 슈퍼 어드민: 전체 창업사관학교와 스타트업 데이터 열람, 시스템 설정
- 서브 어드민(창업사관학교): 자기 소속 스타트업만 관리, 프로그램 등록
- 테넌트(스타트업): 자사 정보와 신청한 프로그램만 확인
이 구조를 DB로 표현하면:
CREATE TABLE roles (
id UUID PRIMARY KEY,
name VARCHAR(50), -- 'super_admin', 'tenant_admin', 'user'
tenant_id UUID, -- NULL이면 플랫폼 레벨 역할
permissions JSONB -- {"read": true, "write": true, "delete": false}
);
빌링(과금) 구조 설계
SaaS의 과금은 테넌트 단위로 이루어집니다. Lotus Soft에서 계정 유료화 버전에 따라 접근 가능한 기능이 달라지는 구조를 구현했습니다.
플랜 테이블:
CREATE TABLE plans (
id UUID PRIMARY KEY,
name VARCHAR(50), -- 'free', 'pro', 'enterprise'
features JSONB, -- 플랜별 가능 기능 목록
price_monthly DECIMAL
);
CREATE TABLE tenant_subscriptions (
tenant_id UUID REFERENCES tenants(id),
plan_id UUID REFERENCES plans(id),
started_at TIMESTAMP,
expires_at TIMESTAMP
);
기능 제한 체크:
// 미들웨어에서 플랜 기능 확인
const canUseFeature = async (tenantId, feature) => {
const subscription = await getTenantSubscription(tenantId);
return subscription.plan.features[feature] === true;
};
테넌트 온보딩 자동화
새 고객(테넌트)이 가입했을 때 자동으로 설정이 완료되어야 합니다.
온보딩 프로세스:
- 테넌트 레코드 생성
- 어드민 계정 생성 및 이메일 발송
- 기본 플랜 적용
- 초기 데이터 세팅 (샘플 데이터 또는 빈 상태)
- 웰컴 이메일 발송
이 과정을 수동으로 하면 실수가 생기고 시간이 걸립니다. 가입 즉시 자동화되는 파이프라인을 처음부터 설계해야 합니다.
실제 개발 시 자주 발생하는 버그
버그 1 — tenant_id 필터 누락: 쿼리에서 tenant_id를 빠뜨려 다른 테넌트 데이터가 노출. 반드시 모든 쿼리에 자동으로 tenant_id를 주입하는 미들웨어 레이어를 만들어야 합니다.
버그 2 — 공유 리소스 캐시 충돌: 레디스 캐시 키에 tenant_id를 포함하지 않으면 A 테넌트 캐시가 B 테넌트에 노출됩니다. 캐시 키는 반드시 {tenant_id}:{resource_type}:{id} 형식으로.
버그 3 — 파일 저장소 분리 미흡: S3처럼 파일을 저장할 때 테넌트별 폴더 구조를 만들지 않으면 파일이 섞입니다. s3://bucket/{tenant_id}/files/{filename} 구조를 처음부터 적용하세요.
마치며
멀티테넌트 SaaS는 처음부터 올바르게 설계하지 않으면 나중에 수정하는 비용이 막대합니다. 특히 데이터 격리와 권한 관리는 보안 사고로 이어질 수 있어 더욱 중요합니다.
인베스트리, Lotus Soft, Athena 세 프로젝트를 통해 확인한 것은, “완벽한 멀티테넌트 설계는 없지만, 처음에 어떤 방식을 선택할지 팀 전체가 합의하고 일관되게 지키는 것”이 가장 중요하다는 점입니다.
무료 상담
비슷한 플랫폼을 만들고 계신가요?
디비컨설팅은 100건 이상의 웹·앱·플랫폼 구축 경험이 있습니다. 기획 단계에서 놓치기 쉬운 부분부터 함께 점검해 드립니다.
기획서 없이 문의하셔도 됩니다 · 영업일 기준 24시간 이내 회신 · 상담 무료


