SaaS 서비스 운영 중 아찔한 제보가 들어옵니다. "A사 관리자 화면에서 B사 데이터가 보여요." 한편 신규 고객사 온보딩은 수동이라 3일이 걸리고, 해지한 고객의 계정이 아직 살아 있습니다. "Enterprise 플랜 기능을 Basic 고객이 쓰고 있다"는 버그도 있습니다. PM·인프라인 당신은 멀티테넌트·권한 용어를 알아야 이 문제들의 심각도와 방향을 판단합니다. 이 사전은 SaaS·멀티테넌트·권한 용어를 빠르게 해독합니다.
- 1멀티테넌트의 데이터 격리 과제와 방식을 설명할 수 있다
- 2RBAC·ABAC·권한 모델로 접근 제어를 구분할 수 있다
- 3프로비저닝·디프로비저닝으로 테넌트/계정 생명주기를 이해할 수 있다
- 4기능 플래그·화이트라벨로 테넌트별 차등 제공을 설명할 수 있다
테넌트 · 격리
확대
위 그림처럼 Row-Level 격리는 비용이 낮지만 쿼리 실수로 데이터 노출 위험이 있고, DB 분리는 완전한 격리를 제공하지만 비용이 높습니다. 격리 실패는 단순 버그가 아니라 치명적 보안 사고입니다.
여럿이 공유하되 서로 안 보이게
| 용어 | 한 줄 뜻 | 비고 | 중요도 |
|---|---|---|---|
| Tenant / Multi-Tenant / Single Tenant | 고객사 단위 / 공유 / 전용 | 격리가 핵심 | ★★★ |
| Tenant Isolation / Data Isolation | 테넌트/데이터 격리 | 실패=보안사고 | ★★★ |
| Company ID / Member ID / User ID | 회사/회원/사용자 식별 | 쿼리에 tenant 조건 강제 | ★★★ |
| Organization / Account Sync / Department Code / Mapping Table | 조직 동기화·매핑 | 외부 조직 연동 | ★★ |
| Provisioning / Deprovisioning | 생성 / 정리 | 온보딩·해지 자동화 | ★★ |
| Tenant Config | 테넌트별 설정 | 커스터마이징 | ★★ |
핵심: 멀티테넌트의 1순위 위험은 격리 실패(A사가 B사 데이터를 봄)입니다. 모든 쿼리에 tenant_id 조건을 강제하거나 스키마/DB 분리로 격리합니다. 격리 누락은 단순 버그가 아니라 치명적 보안 사고입니다(SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화).
권한 — 인증·인가·모델
확대
위 그림처럼 RBAC은 역할에 권한을 묶어 단순하고 명확하며, ABAC은 사용자·자원·환경 속성의 조합으로 동적 판단합니다. 대부분 RBAC으로 시작해 세밀한 제어가 필요한 곳에 ABAC을 더합니다.
누구인지, 무엇을 할 수 있는지
| 용어 | 한 줄 뜻 | 비고 | 중요도 |
|---|---|---|---|
| Authentication / Authorization | 인증(누구) / 인가(권한) | 401 vs 403 → 용어사전 | ★★★ |
| Role / Permission | 역할 / 권한 | RBAC의 기본 | ★★ |
| RBAC / ABAC | 역할기반 / 속성기반 접근제어 | RBAC로 시작 | ★★ |
| Admin Role / Super Admin | 관리자 / 최상위관리자 | 권한 분리 → SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화 | ★★ |
| Access Control | 접근 제어 | 최소권한 원칙 | ★★ |
핵심: 인증(Authentication)은 '누구인지', 인가(Authorization)는 '무엇을 할 수 있는지'입니다 — 401(미인증)과 403(권한없음)의 구분과 같습니다(용어사전). RBAC으로 역할에 권한을 묶고, 최소 권한 원칙(필요한 것만)을 지킵니다.
차등 제공 — 플래그·화이트라벨
확대
위 그림처럼 기능 플래그로 Basic·Pro·Enterprise 플랜마다 다른 기능을 켜고 끕니다. 코드 재배포 없이 특정 테넌트에만 기능을 활성화하거나, 버그 발생 시 즉시 Kill Switch로 끌 수 있어 배포와 노출을 분리할 수 있습니다.
테넌트·플랜마다 다르게
| 용어 | 한 줄 뜻 | 비고 | 중요도 |
|---|---|---|---|
| Feature Flag / Config Flag / Option Flag | 기능 / 설정 / 옵션 토글 | 플랜별 차등 → 릴리스 전략 | ★★ |
| White Label / Customizing | 고객 브랜드로 제공 / 맞춤 | 테넌트별 UI | ★★ |
| Standard Feature / Custom Menu | 표준 기능 / 맞춤 메뉴 | 공통 vs 개별 | ★ |
핵심: 기능 플래그로 플랜(Basic/Pro/Enterprise)·테넌트별 기능을 켜고 끕니다(릴리스 전략). "Basic 고객이 Enterprise 기능 사용" 버그는 보통 플래그/권한 체크 누락 — 플래그 평가에 테넌트 플랜이 반영되는지 확인합니다.
격리·권한 점검 — 직접 확인
SaaS의 가장 위험한 두 가지 — 격리와 권한 — 를 점검합니다.
1) 데이터 격리: 모든 조회 쿼리에 tenant_id(회사) 조건이 있나?
→ 한 곳이라도 빠지면 타 테넌트 데이터 노출 위험
2) 권한 체크: 화면/API마다 역할(RBAC) 검증이 있나?
→ URL 직접 호출로 권한 우회 가능한지(서버측 검증 필수)
3) 기능 플래그: 플랜별 기능이 테넌트 플랜으로 평가되나?
→ Basic이 Pro 기능 못 쓰는지
점검 결과 예:
주문 조회 쿼리에 tenant_id 조건 누락 1건 → 즉시 수정(격리 사고 직전)
관리자 API가 클라이언트 측만 권한 체크 → URL 직접 호출로 우회 가능
Enterprise 기능 플래그가 플랜 무시하고 ON → Basic도 사용 중
→ 격리·서버측 권한·플래그 평가 모두 보강 필요
echo '쿼리 tenant 조건 + 권한 매트릭스 + 플래그 평가 확인'- 모든 데이터 조회에 tenant_id(회사) 조건이 강제되는지 — 한 쿼리라도 빠지면 타 테넌트 노출. 격리는 "기본값으로 막고 예외만 열기"
- 권한 체크가 클라이언트(화면)에만 있고 서버에 없으면 URL 직접 호출로 우회 가능 → 인가는 반드시 서버측에서(SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화)
- 기능 플래그가 테넌트 플랜을 반영하는지 — "Basic이 Pro 기능 사용"은 플래그 평가에 플랜 누락 신호(릴리스 전략)
- 해지(디프로비저닝) 후에도 계정·토큰이 살아 있으면 보안 위험 → 해지 시 접근 즉시 차단·정리되는지 확인
상황: 새 기능의 조회 쿼리 하나에 WHERE tenant_id = ? 조건을 빠뜨려, A사 사용자에게 B사 데이터가 보이는 심각한 격리 사고가 발생합니다.
원인: 테넌트 격리를 개별 쿼리에 수동 의존했습니다. 모든 쿼리에 일일이 tenant 조건을 넣다 보면 한 곳은 반드시 빠집니다 — 사람이 매번 기억해야 하는 구조 자체가 위험합니다.
진단:
# tenant_id 조건 없는 조회 쿼리 흔적(코드 스캔)
grep -rnE "SELECT .* FROM (orders|users|documents)" src/ | grep -vi "tenant"
해결: (1) 즉시 누락 쿼리 수정 + 영향 범위(누가 무엇을 봤나) 조사·공지. (2) 구조적 방어 — ORM/프레임워크 레벨에서 tenant 조건을 자동 주입(전역 필터·RLS Row-Level Security)해 '깜빡해도 안전'하게. PostgreSQL RLS나 ORM 글로벌 스코프가 대표적. (3) 격리 테스트를 CI에 추가(테스트 전략) — "A 토큰으로 B 데이터 조회 시 0건"을 자동 검증. 멀티테넌트 격리는 사람의 주의가 아니라 시스템으로 보장해야 합니다(SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화).
심화 — 격리 경계는 DB 밖에도 있다
심화: 테넌트 경계의 전체 지도 — DB를 막은 다음에 뚫리는 곳
격리를 'DB 쿼리의 tenant_id 문제'로만 이해하면, DB를 완벽히 막은 뒤 다른 층에서 새는 사고를 만납니다. 테넌트 데이터가 흐르는 모든 층이 경계입니다.
- 격리 경계의 전체 목록: DB(RLS·전역 필터) 외에도 캐시 키, 검색 인덱스, 파일 스토리지 경로, 백그라운드 잡이 물려받는 테넌트 컨텍스트, 에러 리포트에 섞여 나가는 데이터까지 — 테넌트 데이터가 지나가는 층마다 tenant_id가 경계에 포함돼야 합니다. DB 격리를 마친 팀의 다음 사고는 대부분 캐시와 검색 인덱스에서 납니다.
- 면접의 다음 질문 — "대형 고객이 전용 DB를 요구하면?": 격리 방식 3가지를 아는 것 다음은 하이브리드 판단입니다. 실무 답은 '기본은 행 분리, 대형·규제 고객만 DB 분리'인데, 분리한 수만큼 마이그레이션·백업·모니터링·배포가 테넌트 단위로 늘어난다는 운영 비용을 함께 말해야 완결된 답입니다.
- noisy neighbor의 다음 단계 — 무엇을 제한할 것인가: 요청 수(rate limit)만 제한하면 무거운 쿼리 한 방이 그 그물을 빠져나갑니다. 요청 수·쿼리 비용·저장량·동시 백그라운드 잡 수처럼 자원 축별로 쿼터를 나눠야 공정한 분배가 됩니다.
- 격리를 '의도적으로 뚫는 문'의 관리: CS·운영팀용 테넌트 전환(고객 화면 대신 보기) 기능은 격리를 우회하는 정식 통로입니다. 사유 기록·감사 로그·시간 제한 없이 만들면 외부 침입이 아니라 내부 접근이 사고 경로가 됩니다(SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화).
격리는 한 번 구현하고 끝나는 기능이 아니라, 새 저장소·새 캐시가 추가될 때마다 다시 물어야 하는 질문입니다 — "여기에도 테넌트 경계가 있는가?"
상황: 지난 격리 사고 후 RLS와 전역 필터를 도입해 DB 격리를 마쳤습니다. 그런데 "다른 회사 수치가 보인다"는 제보가 다시 들어옵니다. 쿼리 로그를 다 뒤져도 tenant 조건은 전부 정상입니다.
원인: 대시보드 응답을 캐시하면서 캐시 키를 URL 경로로만 만들었습니다. A사 요청이 먼저 캐시를 만들면, 같은 URL을 호출한 B사가 그 캐시를 그대로 히트합니다. DB는 완벽히 격리됐지만 캐시 계층이 테넌트 경계 밖에 있었던 것입니다.
진단:
1) 서로 다른 테넌트 계정 2개로 같은 API를 연속 호출
→ 두 번째 응답에 첫 테넌트의 데이터가 오면 캐시 오염 확정
2) 캐시 키 목록에서 테넌트 식별자 없는 키를 스캔
→ dashboard:summary 처럼 테넌트 구분 없는 키 = 전부 후보
해결: (1) 오염 가능성이 있는 캐시를 전체 무효화하고, 캐시 생성·조회 기록으로 노출 범위를 조사해 공지합니다. (2) 캐시 키에 tenant_id 접두사를 공통 캐시 유틸에서 자동 주입해, 개별 개발자가 기억하지 않아도 격리되게 만듭니다 — DB 전역 필터와 같은 원리를 캐시에 적용하는 것입니다. (3) 격리 자동 테스트를 캐시가 켜진 상태에서도 돌도록 확장합니다(테스트 전략) — 캐시를 끄고 돌린 격리 테스트는 이 사고를 영원히 잡지 못합니다.
인프라/플랫폼으로서 멀티테넌트 격리는 아키텍처 결정입니다 — Row-Level Security·스키마 분리·전역 필터로 '구조적 격리'를 설계하고(SQL Injection 예방과 최소 권한 접근 제어, 데이터 암호화), 프로비저닝/디프로비저닝을 자동화합니다. PM은 격리 실패가 '버그'가 아니라 '치명적 보안 사고'임을 인지하고, 요구사항(요구사항 정의)에 테넌트 격리·권한·플랜별 기능을 인수 기준으로 명시합니다. SaaS에서 신뢰의 근간은 "내 데이터가 남에게 안 보인다"이며, 이는 한 줄의 쿼리 누락으로도 무너지므로 시스템적 방어가 필수입니다.
다음 용어사전에서는 문서·전자서명 서비스 도메인 용어를 정리합니다.
용어 식별 실습으로 굳히기: 용어 식별 — SaaS / 멀티테넌트 / 권한 — 증상·문구를 보고 SaaS·테넌트 격리·권한 용어를 가려냅니다.