멀티테넌트 회원·인증 설계 5편 – B2B SaaS에서 테넌트 분리·스케일링 전략

·2025년 11월 27일
post-thumbnail

🧬 개요

1~4편에서는 주로 도메인/인증/권한 구조를 다뤘다.

  • 1편 – tenant / user / tenant_user 패턴
  • 2편 – JWT + Redis 인증 구조
  • 3편 – 멀티테넌트 RBAC 설계
  • 4편 – 통합회원 플랫폼 실전 적용기 + 메뉴 기반 권한

이제부터는 “이걸 실제로 B2B SaaS로 키워 나갈 때” 생기는 고민들로 넘어가 보려고 한다.

같은 코드와 인프라 위에 여러 테넌트(고객사/입점사)를 올려두면,
언젠가 반드시 부딪히는 질문이 있다.

  1. 데이터는 어디까지 함께 두고, 어디부터 분리할까?
  2. 특정 테넌트가 과도하게 사용하면 다른 테넌트는 어떻게 보호하지?
  3. 비용과 리소스를 어떻게 테넌트 단위로 관리할까?

이 글은 이런 질문들에 대해:

  • 데이터 분리 레벨 (DB/스키마/컬럼)
  • 트래픽 스케일링 & Rate Limit
  • 운영/비용/보안 관점에서의 트레이드오프
  • 단계적으로 분리해 나가는 현실적인 전략

까지 정리해 보는 글이다.


✨ TL;DR

  • 멀티테넌시에서 고민할 축은 크게 네 가지다.
    1. 데이터 분리 수준 (컬럼/스키마/DB/클러스터)
    2. 트래픽·리소스 격리 (noisy neighbor 방지)
    3. 운영/배포/버전 관리
    4. 보안·규제·고객 요구
  • 데이터 분리 전략은 보통 이렇게 단계적으로 간다.
Lv1. 단일 DB, 단일 스키마, tenant_id 컬럼
Lv2. 단일 DB, 테넌트별 스키마 분리
Lv3. 테넌트별 별도 DB (같은 클러스터)
Lv4. 테넌트별 별도 클러스터/프로젝트 (물리적 분리)
  • 트래픽/리소스 측면에서는

    • API Gateway 레벨의 테넌트별 Rate Limit/Quota,
    • 배치/백그라운드 작업의 테넌트별 큐/우선순위,
    • CPU/메모리 모니터링에서 테넌트별 사용량을 따로 보는 것
      이 중요하다.
  • “처음부터 모든 테넌트를 물리적으로 분리”하기보다는:

    기본은 Lv1(공유 DB + tenant_id)로 시작하고,
    부담이 큰 테넌트부터 Lv3/Lv4로 점진적으로 분리하는 전략이
    엔지니어링/비용/운영 측면에서 가장 현실적이다.


1. 멀티테넌시에서 꼭 짚고 넘어갈 네 가지 축

멀티테넌시를 인프라/스케일링 관점에서 바라보면,
고민 포인트는 대략 네 가지 축으로 정리할 수 있다.

  1. 데이터(Data)

    • 다른 테넌트의 데이터가 섞여 보이면 안 된다.
    • 필요하다면 아예 물리적으로 분리되어야 할 수도 있다.
  2. 트래픽 / 리소스(Resource)

    • 한 테넌트가 폭주하면 다른 테넌트가 같이 느려지는
      이른바 noisy neighbor 문제를 어떻게 막을 것인가?
  3. 운영 / 배포(Ops)

    • 코드 버전, 마이그레이션, 배치 작업, 장애 대응 등을
      테넌트별로 어느 정도까지 분리해서 보고 싶은가?
  4. 보안 / 규제(Security & Compliance)

    • 특정 고객사는 데이터 저장 위치, 암호화, 접근 통제 등에
      더 높은 수준을 요구할 수 있다.

이 네 가지 축을 염두에 두고,
각 레벨의 분리 전략을 선택하게 된다.


2. 데이터 분리 전략 – 컬럼부터 클러스터까지

2.1 Lv1 – 단일 DB, 단일 스키마, tenant_id 컬럼

가장 기본이 되는 형태다.

user
---------
id
tenant_id
name
...

order
---------
id
tenant_id
user_id
amount
...
  • 장점

    • 설계/개발이 가장 단순하다.
    • 조인 쿼리와 인덱스 전략이 단일 스키마 기준으로 깔끔하다.
    • 초기에는 운영/모니터링도 쉬운 편이다.
  • 단점

    • 모든 테넌트의 데이터가 한 DB 인스턴스에 올라간다.
    • 특정 테넌트 데이터만 별도로 백업/이동/삭제하기 어렵다.
    • DB 레벨에서 테넌트별 리소스 격리가 거의 없다.

언제 좋냐?

  • “일단 멀티테넌트 기능을 빠르게 검증해야 하는 초기 단계”
  • 소규모 테넌트들만 존재하고,
    아직 특정 대형 고객이 압도적인 트래픽/데이터를 사용하지 않는 시점

2.2 Lv2 – 단일 DB, 테넌트별 스키마 분리

이번에는 DB 인스턴스는 하나지만,
테넌트마다 스키마를 나누는 방식이다.

db_main
  ├─ schema_common   -- 공통 메타/코드/로그 등
  ├─ schema_tenant_a -- tenant A용 테이블들
  └─ schema_tenant_b -- tenant B용 테이블들
  • 장점

    • 테이블을 스키마 단위로 깔끔하게 나눌 수 있다.
    • 특정 테넌트만 백업/복구/마이그레이션 하기 편하다.
    • SQL 권한으로 스키마 단위 접근 제어도 가능하다.
  • 단점

    • 테넌트 수가 많아지면 스키마가 폭증한다.
    • 테이블 구조 변경(DDL)을 테넌트별로 따로 해야 할 수 있다.
    • ORM/마이그레이션 툴 설정이 복잡해질 수 있다.

언제 좋냐?

  • 테넌트 수가 많지 않고,
    특정 고객의 데이터에 대해 “스키마 단위 관리”가 필요한 경우
  • DB는 하나로 운용하되, 논리적인 분리와 백업 단위를 확보하고 싶을 때

2.3 Lv3 – 테넌트별 별도 DB (같은 클러스터)

이제는 아예 테넌트별 DB 인스턴스를 쪼개는 수준이다.
(혹은 같은 DB 클러스터 안에서 테넌트별 데이터베이스를 분리)

db_cluster
  ├─ db_tenant_a
  ├─ db_tenant_b
  └─ db_tenant_c
  • 장점

    • 테넌트별로 성능/용량/백업/복구를 완전히 나눠서 관리할 수 있다.
    • noisy neighbor 문제를 줄이는 데 효과적이다.
    • 특정 테넌트의 데이터만 따로 옮기거나 삭제하기 쉽다.
  • 단점

    • DB 수가 늘어날수록 운영/모니터링 비용이 커진다.
    • 코드에서 “어떤 테넌트 → 어떤 DB 커넥션” 매핑을 관리해야 한다.
    • 공통 조회(여러 테넌트 한 번에 조회)는 애플리케이션 레벨에서
      fan-out 쿼리를 해야 해서 복잡해질 수 있다.

언제 좋냐?

  • 일부 테넌트가 데이터/트래픽을 많이 쓰는 헤비 유저인 상황
  • SLA, 성능, 백업/복구 정책을 테넌트별로 조금씩 다르게 가져가야 하는 경우

2.4 Lv4 – 테넌트별 별도 클러스터/프로젝트

마지막은 아예 인프라 레벨에서 테넌트를 분리하는 방식이다.

  • 각 테넌트마다

    • 별도 DB 클러스터,
    • 별도 애플리케이션 인스턴스,
    • 별도 VPC 또는 네트워크 세그먼트
      를 두는 형태.
  • 장점

    • 보안/규제 요구가 높은 고객에게 “완전한 격리”를 줄 수 있다.
    • 리소스와 비용을 테넌트 단위로 측정/청구하기 쉬워진다.
  • 단점

    • 사실상 “여러 개의 설치형(on-prem) 버전”을 운영하는 느낌이 된다.
    • 배포/마이그레이션/모니터링이 N배로 늘어난다.

언제 좋냐?

  • 금융/공공/대기업처럼 강한 데이터 격리와 전용 인프라를 요구하는 고객
  • 특정 테넌트의 규모가 커져서,
    사실상 별도 서비스처럼 다루는 것이 자연스러운 시점

3. 트래픽 스케일링 – noisy neighbor를 어떻게 막을까

데이터를 어떻게 나누든,
트래픽 관점에서는 항상 noisy neighbor 문제가 따라온다.

한 테넌트가 갑자기 대량의 API를 호출하거나,
대량의 배치 작업을 돌리면
다른 테넌트의 응답 속도와 가용성이 함께 떨어지는 문제.

이를 막기 위해 쓸 수 있는 몇 가지 패턴을 정리해보면:

3.1 API Gateway 레벨 Rate Limit / Quota

  • 테넌트별로:

    • 초당 요청 수 (RPS),
    • 분당/시간당 요청 수,
    • 동시 요청 수 등을 제한한다.
  • 유료 플랜/무료 플랜에 따라 Quota 를 다르게 가져갈 수도 있다.

예시 정책:

  • 기본 플랜: 100 RPS, 일일 10만 호출
  • 프리미엄 플랜: 500 RPS, 일일 100만 호출
  • 내부 관리/운영 API는 별도 도메인/라우트로 분리

  • Rate Limit은 “전역 한도 + 테넌트별 한도”를 함께 가져가는 게 안전하다.
  • 과도하게 제한하면 기능이 제대로 동작하지 않으니,
    실제 트래픽 패턴을 보면서 점진적으로 조정하는 게 좋다.

3.2 배치/백그라운드 작업의 테넌트별 큐

실무에서 자주 놓치는 포인트 중 하나는 배치/백그라운드 작업이다.

  • 알림 발송,
  • 정산/통계,
  • 데이터 동기화,
  • 외부 API 연동 등

이런 작업들이 한 테넌트에서만 폭증해도
전체 시스템에 부담을 줄 수 있다.

그래서:

  • 테넌트별 큐 또는
  • 공용 큐지만 메시지에 tenant_id + 우선순위를 두고
  • Consumer 레벨에서 테넌트별 rate 제한을 둔다.

예:

  • 기본 플랜 테넌트: 초당 5건 처리
  • 프리미엄 플랜 테넌트: 초당 50건 처리
  • “운영자 전용/Test 테넌트”는 별도 큐로 분리

3.3 모니터링에서 “테넌트별 지표”는 필수

  • 전체 RPS, 전체 에러율만 보는 것으로는
    noisy neighbor 문제를 찾기 어렵다.

  • 최소한 다음 정도는 테넌트별로 모니터링하는 것을 추천한다.

    • 테넌트별 요청 수 / 에러율 / 평균 응답 시간
    • 테넌트별 배치 처리량 / 실패율
    • 테넌트별 데이터 용량 / 쿼리 빈도

이를 위해 로그/메트릭에 항상 tenant_id 태그를 붙여 두는 것이 중요하다.


4. 운영/배포 관점 – 한 코드, 여러 테넌트

인프라는 분리해도,
대부분의 멀티테넌트 SaaS는 코드 베이스는 하나를 유지하고 싶어 한다.

여기서 자주 나오는 고민이:

  1. 테넌트별로 기능 ON/OFF를 다르게 가져갈 것인가? (Feature Flag)
  2. 마이그레이션/스키마 변경을 테넌트별로 순차 적용할 것인가?
  3. 장애가 특정 테넌트에만 영향을 주도록 격리할 수 있는가?

4.1 Feature Flag / Tenant Config

  • 테넌트별로:

    • 사용할 수 있는 기능 세트,
    • UI/브랜딩,
    • 보안 옵션(MFA 필수 여부 등)을 다르게 가져갈 수 있다.
  • 이때는 “Tenant Config 서비스”를 하나 두고

    • tenant_id 기준으로 설정을 내려주는 패턴을 많이 쓴다.

4.2 마이그레이션 전략

  • Lv1 구조(공유 DB + tenant_id)에서 시작했다가
    나중에 일부 테넌트를 Lv3(별도 DB)로 옮길 때,

  • 보통은 이런 순서로 간다.

    1. 새 DB/스키마 준비
    2. 백그라운드로 데이터 복제 (CDC 또는 배치)
    3. 일정 시점에 “읽기/쓰기 전환” (short freeze + cut-over)
    4. 일정 기간 dual-read/dual-write로 검증 후 기존 데이터 제거
  • 이 과정에서 테넌트별 DataSource(또는 커넥션 정보)를
    동적으로 바인딩하는 레이어가 필요하다.


5. 보안/규제/고객 요구 – 언제 “분리”가 필수인가

마지막으로, “분리하면 좋다”가 아니라
“분리해야만 한다” 가 되는 경우도 있다.

예를 들어:

  • 금융/공공 고객이

    • 데이터가 어떤 리전에 저장되는지,
    • 어떤 네트워크 경로를 통해 접근 가능한지,
    • 다른 고객과 물리적 리소스를 공유하는지
      명확한 보장을 요구할 때
  • 특정 고객과의 계약 상

    • “전용 DB 클러스터”
    • “전용 VPC”
    • “전용 키 관리/KMS”
      등이 명시되어 있을 때

이 경우에는 사실상 Lv4(별도 클러스터/프로젝트)가 된다.
코드는 최대한 공유하되,
인프라와 데이터는 독립된 설치형처럼 다뤄야 한다.


6. 단계별로 나누어 가는 현실적인 전략

지금까지 내용들을 정리하면,
개인적으로는 멀티테넌트 B2B SaaS를 이렇게 가져가는 걸 선호한다.

1단계 – Shared Everything
  - 단일 DB + tenant_id 컬럼
  - 기본적인 Rate Limit + 모니터링
  - 테넌트 수/트래픽이 아직 크지 않을 때

2단계 – Logical Isolation 강화
  - 테넌트별 사용량 모니터링, 배치 큐 분리
  - 일부 중요한 테넌트에 대해 스키마/DB 분리 시범 적용

3단계 – Heavy Tenant 분리
  - 트래픽/데이터가 큰 테넌트를 Lv3(Lv4)로 분리
  - DataSource 라우팅, 마이그레이션 자동화

4단계 – 규제/특수 고객 대응
  - 보안/규제 요구가 높은 고객은 별도 클러스터/프로젝트
  - 공통 코드베이스 + 템플릿 인프라로 반복 구축

핵심은:

“처음부터 모든 걸 완벽하게 나누지 않고,
데이터/트래픽 패턴을 보면서 점진적으로 분리해 나가는 것”

이다.


✅ 마무리

이번 5편에서는
멀티테넌트 회원·인증 구조 위에

  • 데이터 분리 레벨 (컬럼/스키마/DB/클러스터),
  • noisy neighbor를 막기 위한 Rate Limit/큐 구조,
  • 운영/배포/보안 관점에서의 트레이드오프,
  • 단계적인 분리 전략

을 정리해 보았다.

1~4편이 “어떻게 모델링하고 인증/권한을 설계할 것인가”에 가까웠다면,
5편은 “그 구조를 실제 B2B SaaS로 키워 나갈 때 어떤 선택을 하게 되는가”에 대한 이야기다.

다음 편(6편)에서는
지금까지의 내용을 한 번에 정리하면서,

  • “우리 서비스에 정말 멀티테넌트가 필요한가?” 체크리스트
  • 설계/구현에서 자주 나오는 안티패턴
  • 프로젝트 초기에 어떤 수준까지 고민하면 좋은지

를 한 번에 정리해볼 예정이다.


참고 문헌

  1. Microsoft Docs, “Multitenant SaaS database tenancy patterns”, Azure SQL Database.

  2. BIX Tech, “Multi-Tenant Architecture: The Complete Guide for Modern SaaS and Analytics Platforms”.

  3. Azure Architecture Center, “Noisy Neighbor Antipattern”.

  4. AWS Architecture Blog, “Throttling a tiered, multi-tenant REST API at scale using API Gateway”.

  5. DreamFactory, “Rate Limiting vs Throttling: Multi-Tenant API Use Cases”.

  6. Neon / Medium 등, “Noisy Neighbor Problem in Multi-Tenant Systems” 관련 글들.

profile
하나씩 꽉꽉 눌러 담는 실무 개발 노트 ✍️📦

0개의 댓글