public 스키마
목적
public 스키마는 Lumie의 주요 런타임 스키마입니다. 이 안에는 플랫폼 범위 테이블과 tenant 범위 테이블이 모두 들어 있습니다. tenant 격리는 별도 스키마로 모델링되지 않으며, tenant_id 컬럼, 백엔드 tenant 컨텍스트, PostgreSQL RLS policy로 강제됩니다.
이 페이지는 persistence를 변경하거나 특정 테이블이 플랫폼 범위인지 tenant 범위인지 검토하는 백엔드 개발자를 위한 reference 문서입니다.
소스 경로
| 경로 | 역할 |
|---|---|
V1__create_platform_tables.sql | tenants, tenant_settings 생성 |
V13__create_event_publication.sql | Spring Modulith event publication 저장소 |
V14__create_shedlock.sql | 스케줄 작업 lock 테이블 |
V16__create_idempotency_keys.sql | 요청 idempotency 테이블 |
V18__rls_baseline.sql | public에서 tenant 범위 도메인 테이블을 RLS와 함께 재구성 |
V27__langgraph_schema.sql | 별도 langgraph 스키마와 role 생성 |
lumie-backend/libs/common/src/main/java/com/lumie/common/tenant/ | 런타임 tenant 컨텍스트 헬퍼 |
범위 결정
| 테이블 유형 | public에 속함 | RLS 필요 | 전형적 소유자 |
|---|---|---|---|
| Platform catalogue | Yes | No | Tenant, billing, app platform 모듈 |
| Tenant domain data | Yes | Yes | exam, content, attendance 같은 제품 모듈 |
| Outbox and locks | Yes | Usually no | 애플리케이션 인프라 |
| Worker checkpoint data | No | No | langgraph 같은 전용 스키마 |
플랫폼 테이블
플랫폼 테이블은 전역 제어 평면 레코드입니다. Lumie가 tenant 전반에서 관리해야 하므로 tenant RLS로 필터링되지 않습니다.
| 테이블 | 목적 |
|---|---|
tenants | tenant 식별자, slug, lifecycle, 연락처, 브랜딩 필드 |
tenant_settings | tenant별 플랫폼 설정 |
plans | 제품 요금제 카탈로그 |
subscriptions | tenant 구독 상태 |
billing_keys | Toss billing key 메타데이터 |
event_publication | Spring Modulith outbox 테이블 |
shedlock | 스케줄 작업 lock |
idempotency_keys | API idempotency 레코드 |
public 안의 테넌트 범위 테이블
tenant 데이터 테이블은 물리적으로는 public에 있지만 tenant_id를 가지며 RLS로 보호됩니다. 예시는 다음과 같습니다.
- 신원 및 사용자:
users,students,staff,staff_permissions - 교육 모델:
classes,lectures,assignments,textbooks - 시험 및 채점:
exams,exam_results,questions,question_results,omr_grading_jobs - 커뮤니케이션 및 콘텐츠:
announcements,qna_boards,sms_messages,ai_chat_messages - 출결:
attendance_sessions,attendance_records - 파일 메타데이터 및 링크:
file_metadata,file_links,file_download
RLS 계약
tenant 범위 테이블은 다음 패턴을 따릅니다.
ALTER TABLE <table_name> ENABLE ROW LEVEL SECURITY;
ALTER TABLE <table_name> FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <table_name>
USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::bigint)
WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::bigint);
백엔드 코드는 이 테이블을 조회하기 전에 활성 트랜잭션 안에서 app.tenant_id를 설정해야 합니다. tenant 컨텍스트 없이 실행되는 쿼리는 fail closed 되거나 행을 반환하지 않아야 합니다.
검증
public schema를 변경할 때는 다음 검사를 사용하세요.
rg -n "ENABLE ROW LEVEL SECURITY|FORCE ROW LEVEL SECURITY|tenant_isolation" \
lumie-backend/app/src/main/resources/db/migration/public
rg -n "TenantContextHolder|app.tenant_id|SET LOCAL" lumie-backend