Zum Hauptinhalt springen

데이터 모델 개요

스키마 구조

Lumie는 PostgreSQL 16을 사용합니다. 멀티테넌시는 RLS(Row-Level Security) 기반으로 구현됩니다. 모든 테넌트 데이터는 public 스키마의 단일 공유 테이블에 저장되며, 각 행에 tenant_id BIGINT NOT NULL 컬럼을 두고 Postgres RLS 정책으로 행 단위 격리를 강제합니다. Schema-per-tenant(스키마별 테넌트 격리)는 2026-05-12(V18)에 완전 제거되었습니다.

RLS 기반 멀티테넌시

격리 메커니즘

모든 테넌트 스코프 테이블에 다음 패턴이 적용됩니다:

ALTER TABLE <table> ENABLE ROW LEVEL SECURITY;
ALTER TABLE <table> FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <table>
USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::bigint)
WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::bigint);
  • FORCE ROW LEVEL SECURITY: 테이블 소유자(postgres)에게도 정책 적용
  • app.tenant_id: 요청 단위로 SET LOCAL app.tenant_id = <id> 설정
  • lumie_app 역할(NOSUPERUSER NOBYPASSRLS)이 런타임 연결에 사용됨 — BYPASSRLS가 없으므로 RLS 정책이 항상 활성화됨

DB 역할

역할용도RLS
lumie_app애플리케이션 런타임 연결적용됨 (NOBYPASSRLS)
postgresFlyway 마이그레이션 전용SUPERUSER (우회)
lumie_langgraphLangGraph 체크포인터langgraph 스키마만 접근

요청 처리 흐름

  1. Kong/Traefik이 JWT에서 tenant_slug 추출
  2. X-Tenant-Slug 헤더로 백엔드에 전달
  3. TenantContextHolder가 요청 스코프에 (slug, tenantId) 설정
  4. Spring AOP가 SET LOCAL app.tenant_id = <id> 실행
  5. 모든 JPA 쿼리가 RLS 정책에 의해 자동으로 해당 테넌트 행만 반환

스키마별 테이블 분류

분류테이블 수RLS설명
플랫폼 공유 (public)5없음tenants, tenant_settings, owner_directory, federated_identities, shedlock 등
빌링 (public)8없음plans, subscriptions, invoices, billing_keys 등 — 앱 계층에서 접근 제어
테넌트 스코프 (public, RLS)45+있음학원별 업무 데이터 전체

상세 내용은 각 스키마 문서를 참조하세요:

공통 컬럼 패턴

테넌트 스코프 테이블의 공통 컬럼:

컬럼타입설명
idBIGSERIAL자동 증가 기본키
tenant_idBIGINT NOT NULLRLS 필터링 기준 (FK 없음 — 성능 최적화)
created_atTIMESTAMPTZ생성 시각 (UTC)
updated_atTIMESTAMPTZ수정 시각 (UTC)
versionBIGINT낙관적 잠금 (@Version)

Flyway 마이그레이션

마이그레이션 파일은 단일 경로에 위치합니다:

src/main/resources/db/migration/
└── public/ # 단일 public 스키마 — 순차 실행 (V1 ~ V52+)

V18(rls_baseline) 이후 tenant 경로는 제거되었습니다. 자세한 내용은 마이그레이션 가이드를 참조하세요.