Public 스키마
개요
public 스키마에는 두 종류의 테이블이 공존합니다:
- 플랫폼 공유 테이블 — RLS 없음. 앱 계층이 접근 제어 담당 (tenants, owner_directory, federated_identities, shedlock 등)
- 테넌트 스코프 테이블 —
tenant_id+ RLS 정책 (FORCE ROW LEVEL SECURITY) 적용. 학원별 업무 데이터 전체
이 문서는 플랫폼 공유 테이블(RLS 없음)만 다룹니다. 테넌트 스코프 테이블은 Tenant 스키마를 참조하세요.
ERD
tenants
플랫폼 전체 학원(테넌트) 디렉토리. 슬러그(slug)로 라우팅합니다.
| 컬럼 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
id | BIGSERIAL | PK | 테넌트 ID |
slug | VARCHAR(30) | UNIQUE NOT NULL | 테넌트 식별자 (URL-safe, e.g. awesome-academy) |
name | VARCHAR(100) | NOT NULL | 학원명 |
display_name | VARCHAR(200) | 표시 이름 | |
status | VARCHAR(20) | NOT NULL | PENDING, PROVISIONING, ACTIVE, SUSPENDED |
owner_email | VARCHAR(255) | 원장 이메일 | |
plan_id | VARCHAR(20) | NOT NULL | 구독 플랜 ID → plans |
custom_id | VARCHAR(50) | UNIQUE NOT NULL | 학원 커스텀 식별자 (V9, V50에서 NOT NULL) |
institute_name | VARCHAR(200) | 사업체명 | |
business_registration_number | VARCHAR(12) | 사업자등록번호 | |
phone | VARCHAR(30) | 학원 대표 전화 | |
address | VARCHAR(255) | 학원 주소 | |
logo_object_key | VARCHAR(255) | 학원 로고 오브젝트 키 (V8) | |
custom_domain | VARCHAR(253) | UNIQUE | 커스텀 도메인 (수동 설정, V10-11) |
hidden_sidebar_item_ids | JSONB | NOT NULL DEFAULT [] | 학원에서 숨긴 관리자 사이드바 메뉴 ID 목록 (V60) |
version | BIGINT | NOT NULL DEFAULT 0 | 낙관적 잠금 버전 (V17) |
created_at | TIMESTAMPTZ | NOT NULL | 생성 시각 |
updated_at | TIMESTAMPTZ | NOT NULL | 수정 시각 |
schema_name컬럼은 V18에서 제거되었습니다. Schema-per-tenant 전략 폐기 후 불필요해졌습니다.
owner_directory
OWNER 사용자의 경량 라우팅 디렉토리입니다 (V22 신설). RLS가 적용되지 않습니다.
목적: 로그인 시 tenant_id 컨텍스트를 알기 전에 OWNER가 어느 테넌트에 속하는지 찾기 위한 진입점. public.users는 RLS로 보호되어 테넌트 컨텍스트 없이 조회 불가이므로, 이 테이블이 pre-auth 라우팅을 담당합니다.
| 컬럼 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
id | BIGINT | PK (application-supplied) | OWNER의 users.id와 동일 값 |
user_login_id | VARCHAR(50) | UNIQUE NOT NULL | 로그인 ID |
tenant_id | BIGINT | FK → tenants (ON DELETE CASCADE) | 소속 테넌트 |
email | VARCHAR(255) | 이메일 | |
created_at | TIMESTAMPTZ | NOT NULL | 생성 시각 |
updated_at | TIMESTAMPTZ | NOT NULL | 수정 시각 |
federated_identities
OAuth 제공자별 외부 ID 매핑입니다.
| 컬럼 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
id | BIGSERIAL | PK | ID |
user_id | BIGINT | FK → users(id) (ON DELETE CASCADE) | 사용자 |
provider | VARCHAR(20) | NOT NULL | google, kakao 등 |
provider_user_id | VARCHAR(255) | NOT NULL | 제공자 측 사용자 ID |
email | VARCHAR(255) | 제공자 이메일 | |
created_at | TIMESTAMPTZ | NOT NULL | 생성 시각 |
(provider, provider_user_id) 조합에 UNIQUE 제약이 있습니다.
마이그레이션 이력
| 버전 | 내용 |
|---|---|
| V1 | tenants, tenant_settings 생성 |
| V2 | users 생성 (full identity — V18에서 재구조화) |
| V3 | role 값 정규화 |
| V4 | federated_identities 추가 |
| V5 | avatar_seed 추가 |
| V6 | email 추가 |
| V7 | tenant contact 필드 추가 |
| V8 | tenants.logo_object_key 추가, tenant_settings 제거 |
| V9 | ENTERPRISE → MAX 리네임, custom_id 추가 |
| V10 | custom domain 필드 추가 |
| V11 | custom domain manual 모드 단순화 |
| V12 | users OWNER-only 슬림화 (비-OWNER 컬럼 제거) |
| V13 | event_publication (Spring Modulith) |
| V14 | shedlock |
| V15 | 감사 타임스탬프 TIMESTAMPTZ 전환 |
| V18 | RLS baseline — schema-per-tenant 제거, 모든 테넌트 테이블을 public으로 통합 + RLS 정책 적용, schema_name 컬럼 제거 |
| V22 | owner_directory 신설 (pre-auth 라우팅) |
| V26 | admins → staff / admin_permissions → staff_permissions 리네임 |
| V27 | langgraph 스키마 신설 (LangGraph 체크포인터 전용) |