데이터 모델 개요
스키마 구조
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) |
postgres | Flyway 마이그레이션 전용 | SUPERUSER (우회) |
lumie_langgraph | LangGraph 체크포인터 | langgraph 스키마만 접근 |
요청 처리 흐름
- Kong/Traefik이 JWT에서
tenant_slug추출 X-Tenant-Slug헤더로 백엔드에 전달TenantContextHolder가 요청 스코프에(slug, tenantId)설정- Spring AOP가
SET LOCAL app.tenant_id = <id>실행 - 모든 JPA 쿼리가 RLS 정책에 의해 자동으로 해당 테넌트 행만 반환