From c77f9d2fe646c4f25aec3cd7767520b71446fc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=ED=98=95=EC=84=9D?= Date: Wed, 17 Jun 2026 13:32:45 +0900 Subject: [PATCH] Add tenant-policy.md --- tenant-policy.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tenant-policy.md diff --git a/tenant-policy.md b/tenant-policy.md new file mode 100644 index 0000000..fe4e83e --- /dev/null +++ b/tenant-policy.md @@ -0,0 +1,46 @@ +# 테넌트 기본 아키텍처 및 데이터 정책 (Tenant Architecture Policy) + +본 문서는 Baron SSO의 테넌트 다형성(Polymorphism) 모델과 데이터베이스 스키마, 그리고 외부 시스템(Kratos)과의 역할 분리에 대한 핵심 아키텍처 정책을 정의합니다. + +## 1. 테넌트 다형성 모델 (Polymorphic Tenant) + +모든 형태의 격리 공간은 `Tenant`라는 단일 개념(Base Unit)으로 취급되며, 데이터베이스의 `type` 속성을 통해 그 역할이 구분됩니다. + +- **`PERSONAL`**: B2C 개인 워크스페이스. 개인 유저 가입 시 1:1로 자동 생성됩니다. +- **`COMPANY`**: B2B 법인/기업. 독립적인 비즈니스와 사내 조직도를 가집니다. +- **`COMPANY_GROUP`**: B2B2B 지주사/그룹사. 여러 `COMPANY`를 하위로 거느리며 권한을 통합합니다. +- **`USER_GROUP`**: 사내 조직 (본부/팀 등). 과거에는 분리된 개념이었으나, 현재는 완벽한 통합을 위해 테넌트의 한 종류로 1:1 매핑됩니다. + +## 2. Ory SSOT와 Backend read model 분리 + +Kratos 내부 트레이트(Traits)에 테넌트, 직급 등 관계형 데이터를 저장하는 것은 토큰 비대화 및 쿼리 성능 저하를 일으키는 안티 패턴입니다. 하지만 Backend DB를 별도 원장으로 세우지도 않습니다. 권한/관계 판단은 Ory Keto, identity 판단은 Ory Kratos를 기준으로 하고, Backend DB는 Ory에 저장되지 않거나 Ory API로 필요한 조회가 불가능한 조직 표시/검색 데이터의 read model만 보관합니다. + +- **Ory Kratos (Identity SSOT)**: 이메일, 패스워드 등 인증 식별 정보를 저장합니다. +- **Ory Keto (Relationship SSOT)**: 테넌트 소속, 소유, 접근 같은 권한 관계를 저장하고 판정합니다. +- **Backend DB read model**: Ory에 저장되지 않거나 조회가 불가능한 테넌트 표시/검색 metadata, 설정, 외부 연동 상태만 저장합니다. + +## 3. Seed tenant 식별 정책 + +`adminfront/seed-tenant.csv`에 정의된 초기 테넌트는 CSV의 `id`/`tenant_id` UUID를 source of truth로 삼습니다. `slug`는 운영자가 읽고 외부 연동에서 다루기 쉬운 식별자지만, 오타 수정이나 명칭 정책 변경으로 바뀔 수 있으므로 초기 테넌트 보호 여부와 seed 동기화의 최종 식별 기준으로 사용하지 않습니다. + +- 초기 테넌트 여부는 seed CSV의 UUID와 `tenants.id` 일치 여부로 판단합니다. +- `slug`, `name`, `memo`, 도메인 같은 표시/설정 값은 UUID로 식별된 seed tenant의 동기화 대상 metadata입니다. +- 기존 DB row가 seed UUID와 일치하지만 `slug`가 CSV와 다르면, backend bootstrap seed 경로는 CSV의 `slug`로 보정해야 합니다. +- 목표 `slug`를 다른 활성 tenant가 이미 사용 중이면 자동 보정하지 않고 충돌로 처리합니다. +- AdminFront의 “초기 설정” 표시와 삭제 보호도 `slug`가 아니라 seed UUID 기준으로 동작해야 합니다. +- 일반 import/export 정책에서는 운영 편의를 위해 `slug`를 우선 사용할 수 있지만, seed tenant의 identity 보존 및 복구 정책은 UUID 기준을 우선합니다. + +예를 들어 한라산업개발 seed row의 UUID가 `5a03efd2-e62f-4243-800d-58334bf48b2f`이면, 기존 DB의 `slug`가 `hanlla`여도 같은 UUID row는 동일 seed tenant로 보고 CSV 값인 `halla`로 보정합니다. + +## 4. 데이터베이스 스키마 분리 전략 + +테넌트 테이블의 비대화를 막기 위해, Identity(신분증) 역할과 무거운 Business 데이터를 분리 조인(Join)합니다. + +- **`tenants` 테이블**: 최소한의 식별 정보(`id`, `name`, `type`)만 저장하는 초경량 베이스 테이블. +- **`company_settings` 테이블**: `COMPANY` 및 `COMPANY_GROUP` 타입 전용 무거운 비즈니스 설정 (결제 정보, 커스텀 도메인 등). +- **`user_groups` 테이블**: `USER_GROUP` 타입 전용 사내 조직도 메타데이터 (`parent_id`, 조직장 정보 등). + +## 5. 논리적 다중 테넌트 OIDC 관리 (Logical Pooling) + +인프라 비용의 팽창을 막기 위해 테넌트별로 Hydra(OAuth2) 데이터베이스를 물리적으로 복제하는 방식은 금지합니다. +대신 공유되는 소수의 Hydra 클러스터 앞단에 도메인 및 헤더를 재작성하는 지능형 프록시를 배치하고, 백엔드의 동의(Consent) 로직을 통해 요청된 클라이언트의 테넌트 맥락에 맞는 **동적 클레임(Dynamic Claim)**을 ID Token에 주입합니다.