오늘 생각했던 것을 글 하나로 정리해보려고 합니다. 어제 글처럼 간단한 tmux 세팅을 1시간 내에 상쾌하게 마무리했는데, 이번 글은 적다 보니 꽤 길어지네요.
Multi-tenant Software-as-a-Service(SaaS)에서 데이터베이스 아키텍처를 어떻게 잡을 것인지는 상당히 많은 기술적 트레이드오프가 존재합니다.
대부분의 SaaS가 사실 Multi-tenent SaaS 일 것 같아서 Multi-tenant 라는 표현을 붙일 필요가 있나? 싶은데, 그래도 Multi-tenancy가 데이터 측면에서 더 가중치가 있는 SaaS는 분명 있을 것 같고, 그게 B2B SaaS에 더 많을 것 같다는 생각을 합니다. 이 자체로도 좀 생각하고 데이터를 수집할 게 있어서 이정도로 마무리 할게요.
이 AWS re:Invent 발표가 이러한 트레이드오프를 잘 보여주고 있습니다. 발표가 상당히 길어서 infographic으로 요약해 첨부합니다.

각 테넌트(고객사)마다 독립된 데이터베이스 인스턴스나 스토리지를 통째로 할당하는 방식을 Silo Model이라고 합니다.
장점 (Pros)
단점 (Cons)
하나의 인프라(DB 인스턴스, 테이블 등) 안에 여러 테넌트의 데이터를 함께 저장하는 방식입니다.
장점 (Pros)
단점 (Cons)
서비스가 성장하고 고객 수가 증가할 때, 개별 고객이 사용하는 데이터/트래픽 양이 전용 인스턴스를 띄울 만큼 크지 않다면 Pool Model이 합리적이지 않을까 싶습니다. 게다가 트라이얼로 서비스를 쓰는 고객도 얼마나 많을까요? 그런 고객마다 전용 인스턴스와 스토리지를 만드는 건 큰 낭비입니다.
일정 규모 이상 성장한 서비스 회사는 Pool Model이 가져오는 아키텍처 복잡성보다 비용 절감 효과가 더 크기 때문에, 결국 Pool Model로 수렴할 가능성이 높다고 봅니다. 대규모 서비스 기업들이 데이터베이스 엔진을 직접 커스터마이즈하거나 개발하는 이유와 비슷할 것 같습니다.
하지만 Pool Model의 단점 중 복잡한 격리 로직 요구(Complex Isolation Logic)는 실수 한 번이 회사의 존망을 결정할 수 있는 문제입니다. 예를 들어서 WHERE tenant_id = '...' 한 번 빠뜨리면 A 고객 대시보드에 B 고객 매출이 뜬다는 이야기 입니다.
PostgreSQL의 Row-Level Security(RLS)는 바로 이 문제를 해결하기 위해 등장한 기능입니다.
PostgreSQL RLS는 이걸 앱이 아닌 DB 엔진에서 강제하고 있습니다.
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_policy ON orders
USING (tenant_id = current_setting('app.current_tenant')::uuid);
앱은 커넥션 시점에 SET app.current_tenant = 'tenant-A-uuid'만 하면 되고, 이후 모든 쿼리는 자동 필터링됩니다.
PostgreSQL이 RLS를 코어 기능으로 수용한 것은, 전 세계 SaaS 기업들이 공통으로 겪는 '격리 문제'를 엔진 수준에서 해결해주는 것이 생태계 전체의 비용을 낮추는 가장 효율적인 방법이라고 판단했기 때문입니다.
RLS는 단순한 편의 기능이 아닙니다. 어떤 경로(DB 툴, API, 직접 쿼리)로 데이터에 접근하든 데이터 자체가 보안 정책을 집행하게 만드는 'Zero Trust' 관점의 데이터 거버넌스를 가능하게 해주는 기능입니다.