Multi-tenant SaaS 아키텍처와 PostgreSQL RLS

하수구 배관공·2025년 12월 23일

알쓸잡

목록 보기
2/6

오늘 생각했던 것을 글 하나로 정리해보려고 합니다. 어제 글처럼 간단한 tmux 세팅을 1시간 내에 상쾌하게 마무리했는데, 이번 글은 적다 보니 꽤 길어지네요.

Multi-tenant Software-as-a-Service(SaaS)에서 데이터베이스 아키텍처를 어떻게 잡을 것인지는 상당히 많은 기술적 트레이드오프가 존재합니다.

대부분의 SaaS가 사실 Multi-tenent SaaS 일 것 같아서 Multi-tenant 라는 표현을 붙일 필요가 있나? 싶은데, 그래도 Multi-tenancy가 데이터 측면에서 더 가중치가 있는 SaaS는 분명 있을 것 같고, 그게 B2B SaaS에 더 많을 것 같다는 생각을 합니다. 이 자체로도 좀 생각하고 데이터를 수집할 게 있어서 이정도로 마무리 할게요.

AWS re:Invent 발표가 이러한 트레이드오프를 잘 보여주고 있습니다. 발표가 상당히 길어서 infographic으로 요약해 첨부합니다.

1. 사일로 모델 (Silo Model): 테넌트별 전용 자원 할당

각 테넌트(고객사)마다 독립된 데이터베이스 인스턴스나 스토리지를 통째로 할당하는 방식을 Silo Model이라고 합니다.

장점 (Pros)

  • 강력한 격리(Strong Isolation): 데이터가 물리적/논리적으로 완전히 분리되어 있어, 한 테넌트의 실수가 다른 테넌트의 데이터 유출로 이어질 가능성이 거의 없음
  • 설계의 단순함(Simplicity): 애플리케이션 레벨에서 복잡한 테넌트 필터링 로직을 구현할 필요가 없다. 해당 테넌트의 DB에 연결하기만 하면 됨
  • 성능 예측 가능: 특정 테넌트가 갑자기 많은 자원을 사용하더라도 다른 테넌트의 성능에 영향을 주지 않는다. "Noisy Neighbor" 문제가 없음

단점 (Cons)

  • 증가(Costly): 테넌트가 늘어날수록 유휴 자원이 많아지며, DB 인스턴스 개수만큼 비용이 늘어남
  • 관리의 어려움(Hard to Manage at Scale): 수천 개의 테넌트가 있다면 수천 개의 DB 스키마 업데이트와 백업을 관리해야 하므로 운영 난이도가 급격히 높아짐

2. 풀 모델 (Pool Model): 모든 테넌트가 자원 공유

하나의 인프라(DB 인스턴스, 테이블 등) 안에 여러 테넌트의 데이터를 함께 저장하는 방식입니다.

장점 (Pros)

  • 비용 효율 극대화(Cost-efficiency): 인프라 자원을 공유하므로 유휴 자원을 최소화하고 규모의 경제를 달성할 수 있습니다.
  • 높은 확장성(Scalability): 새로운 테넌트가 추가될 때마다 인프라를 새로 구축할 필요 없이 논리적으로만 추가하면 되므로 온보딩이 빠릅니다.
  • 집중 관리: 하나의 시스템만 관리하면 되므로 모니터링, 백업, 소프트웨어 업데이트가 간편합니다.

단점 (Cons)

  • 복잡한 격리 로직 요구(Complex Isolation Logic): 애플리케이션이나 DB 엔진 수준에서 테넌트 데이터를 엄격히 분리해야 합니다. 여기서 실수가 나면 데이터 유출 사고로 이어집니다.
  • Noisy Neighbor 문제: 특정 테넌트의 무거운 쿼리가 전체 시스템 성능을 저하시켜 다른 테넌트에게 피해를 줄 수 있습니다.
  • 운영 복잡성: 공유 환경에서도 개별 테넌트 단위의 데이터 백업 및 복원을 설계해야 하는 기술적 난이도가 있습니다.

풀 모델의 장점과 위험성

서비스가 성장하고 고객 수가 증가할 때, 개별 고객이 사용하는 데이터/트래픽 양이 전용 인스턴스를 띄울 만큼 크지 않다면 Pool Model이 합리적이지 않을까 싶습니다. 게다가 트라이얼로 서비스를 쓰는 고객도 얼마나 많을까요? 그런 고객마다 전용 인스턴스와 스토리지를 만드는 건 큰 낭비입니다.

일정 규모 이상 성장한 서비스 회사는 Pool Model이 가져오는 아키텍처 복잡성보다 비용 절감 효과가 더 크기 때문에, 결국 Pool Model로 수렴할 가능성이 높다고 봅니다. 대규모 서비스 기업들이 데이터베이스 엔진을 직접 커스터마이즈하거나 개발하는 이유와 비슷할 것 같습니다.

하지만 Pool Model의 단점 중 복잡한 격리 로직 요구(Complex Isolation Logic)는 실수 한 번이 회사의 존망을 결정할 수 있는 문제입니다. 예를 들어서 WHERE tenant_id = '...' 한 번 빠뜨리면 A 고객 대시보드에 B 고객 매출이 뜬다는 이야기 입니다.

PostgreSQL의 Row-Level Security(RLS)

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' 관점의 데이터 거버넌스를 가능하게 해주는 기능입니다.

profile
코딩 배우는 하수구 배관공

0개의 댓글