
Keycloak 서버
├── Realm: company-a (A회사 전용 사용자/로그인 설정)
├── Realm: company-b (B회사 전용 사용자/로그인 설정)
└── Realm: master (관리자용)
[로그인/인증 레이어] → Realm (Keycloak, Auth0의 tenant 개념 등)
↓
[애플리케이션 레이어] → JWT에 tenant_id 담아서 요청마다 검증
↓
[DB 레이어] → RLS로 tenant_id 행 필터링
| 구분 | 담당 범위 |
|---|---|
| Realm | 누가 로그인할 수 있는가 (인증 레벨 테넌트 분리) |
| RLS | 로그인한 사람이 어떤 행(row)을 볼 수 있는가 (DB 레벨 테넌트 분리) |
RLS는 DB를 격리하는 여러 방법 중 하나일 뿐이며, 실무에서는 아래 3가지 중 하나(혹은 혼합)를 선택함
| 방식 | 설명 | 격리 강도 | 비용/관리 |
|---|---|---|---|
| Database per tenant | 회사마다 DB 인스턴스 자체를 분리 | 매우 강함 | DB 인스턴스 관리 비용 큼 |
| Schema per tenant | 하나의 DB 안에서 회사마다 스키마 분리 (company_a.orders, company_b.orders) | 강함 | 스키마 개수만큼 마이그레이션 관리 필요 |
| Row-Level (RLS) | 하나의 테이블에 전체 저장, tenant_id로 행만 분리 | 상대적으로 약하지만 관리 편함 | 지금까지 다룬 방식 |
@Filter(Hibernate), Multi-tenancy 지원(CurrentTenantIdentifierResolver) 등을 통해 구현SET app.tenant_id = ? 실행current_setting('app.tenant_id')와 연결되어 필터링 발동Realm(로그인) → JWT → 애플리케이션 → SET app.tenant_id → RLS 정책
SET app.tenant_id = ?는 세션(커넥션) 단위로 유지되는 설정SET을 다시 실행하거나SET LOCAL을 사용해 트랜잭션 범위에서만 유효하게 만들고, 커밋/롤백 시 자동 초기화되게 함-- 트랜잭션 안에서만 유효, 커밋/롤백되면 자동 리셋됨
SET LOCAL app.tenant_id = 'company-a';
tenant_id 값이 Realm(로그인) → JWT → 애플리케이션 → SET app.tenant_id → RLS 정책까지 하나의 흐름으로 이어짐
잘 보고 갑니다