Show Cover Slide
1부. <데이터 격리, 테넌트 계층, 역할 상속, 인가> 모델
이 문서는 IoT 관제 SaaS에서 데이터 격리(multi-tenant separation),
사용자 권한 모델(RBAC/ABAC), 그리고 테넌트 및 역할 상속 구조를
왜 이렇게 고민하게 되었는지를 설명하기 위한 설계 노트입니다.
완성된 설계를 기록하기 위한 문서가 아니라,
설계의 방향이 어떻게 도출되었는지와 현재 남아 있는 미해결 과제를
정리한 문서입니다.
1. 출발점: 왜 이렇게 복잡한 구조를 생각하게 되었는가?
IoT 관제 SaaS는 여러 고객의 장비/사용자/데이터가
단일 플랫폼에서 동시에 운영되는 구조를 가집니다.
여기에는 다음과 같은 상반된 요구가 존재합니다.
- 고객 간 데이터는 완전히 분리되어야 합니다.
- 동일한 고객 내부에는 상위–하위 조직 구조가 존재합니다.
따라서 단일 테넌트 개념만으로는 실제 운영 요구를 충족할 수 없었습니다.
이로 인해 자연스럽게 다음 개념이 도출되었습니다.
- 테넌트 간 부모–자식 구조
- 상위 조직의 하위 테넌트 관제
- 역할(Role)의 상속 구조
- 필요 시 ABAC(Attribute-Based Access Control) 적용
결과적으로 PC 파일시스템의 디렉토리 구조 + Windows ACL 상속
모델에서
많은 아이디어를 차용하게 되었습니다.
2. 데이터는 “문서(Document)” 중심으로 설계한다
IoT 장비/레시피/설정/진단/로그는 모두 트리 구조의 특성을 갖습니다.
그러나 하나의 문서가 지나치게 깊고 복잡한 트리를 갖게 되면:
- 스키마 변경이 어렵고
- 하위 호환성 유지가 어렵고
- 전송 및 저장 효율이 저하됩니다.
이를 해결하기 위해 다음 원칙을 정했습니다.
문서(Document) 원칙
- 모든 데이터는 스키마 기반 문서로 정의합니다.
- 문서는 항상
schemaName + refId조합으로 참조합니다. - 복잡한 트리는 여러 문서로 분리합니다.
- 스키마는 DTO, Validator, Generator를 포함한 구조
계약(Contract)입니다.
이 방식은 테넌트 간 데이터 혼합을 방지하면서
문서 단위의 유연한 확장을 가능하게 합니다.
3. 테넌트(Tenant)는 “트리(Tree)” 구조로 본다
테넌트는 PC의 디렉토리처럼 트리 구조로 관리하는 것이 자연스럽습니다.
예:
/root
/root/BrandA
/root/BrandA/Shop01
/root/BrandA/Shop02
이 구조는 다음 기능을 자연스럽게 제공합니다.
- 상위 테넌트는 하위 테넌트를 전체 관제 가능
- 특정 역할만 “자식 포함” 권한 부여 가능
- 테넌트 경로 기반의 prefix 비교로
조상/자손 판별이 O(1)에 가까운 성능 제공
Tenant 구조 예시
tenantId
parentTenantId
path // materialized path
type // 조직 구분 (예: HQ, branch 등)
attributes // ABAC용 속성
이 구조는 확장성이 높고, 운영/리포트/UI 모두에서 직관적입니다.
4. 역할(Role)은 “객체 상속”과 유사한 구조로 설계한다
실제 현장에서는 역할이 단순 태그 이상의 의미를 가집니다.
예:
TenantViewerTenantOperator= Viewer + 제어 기능TenantOwner= Operator + 사용자 관리 기능
이를 위해 Role에도 부모–자식 상속 구조를 적용했습니다.
Role
- roleId
- parentRoleId
- permissions[]
이를 통해 공통 권한은 부모 역할로 올리고,
추가 권한만 자식에 확장하여 구성할 수 있습니다.
상속 깊이는 운영 복잡도를 고려해 1~2단계가 현실적입니다.
5. UserRole에 “scope” 개념을 도입한다
Windows 디렉토리 권한처럼 역할의 유효 범위를 지정합니다.
UserRole
- userId
- roleId
- scopeTenantId // 기준 테넌트
- scopeType // EXACT | WITH_DESCENDANTS
예시:
- 상위 조직 관리자
→(scope=HQ, WITH_DESCENDANTS) - 특정 매장 관리자
→(scope=Shop01, EXACT)
이를 통해
계층형 테넌트 + 역할 상속 + 데이터 격리 요구를 모두 충족할 수
있습니다.
6. 인가 처리: 요청 시에는 그래프 탐색을 하지 않는다
테넌트 트리와 역할 상속 관계를
요청마다 탐색한다면 성능은 크게 저하됩니다.
핵심 원칙은 다음과 같습니다.
로그인 또는 테넌트 전환 시점에 모든 계산을 완료한다.
절차는 다음과 같습니다.
- 사용자가 특정 테넌트에서 갖는 모든 역할 조회
- 해당 역할들의 상속 구조를 펼쳐(flatten) 전체 권한 집합 구성
- 이 권한들을 Set으로 통합
- 캐시 또는 토큰에 저장
요청 시에는 단순히 다음만 검사합니다.
effectivePermissions.contains(permissionKey)
이 방식은 복잡한 RBAC 구조에서도
일관된 빠른 인가 성능을 제공합니다.
7. 왜 이런 설계를 선택했는가?
이 구조는 복잡한 아키텍처를 보여주기 위한 것이 아닙니다.
오히려 IoT 관제 SaaS의 도메인적 요구가 자연스럽게 요구한 형태입니다.
요구사항 ↔ 구조 대응표
| 요구사항 | 구조로 해결한 방식 |
|---|---|
| 고객 간 완전한 데이터 격리 | tenantId 기반 문서 격리 |
| 본사–지점 구조 | 테넌트 계층(Tree) |
| 역할 차등 | Role 상속 |
| 상위 조직의 하위 관제 | UserRole scope = WITH_DESCENDANTS |
| 빠른 인가 판단 | effectivePermissions 캐싱 |
| 문서 기반 확장성 | 스키마 기반 Document 모델 |
결국 이 설계는
<도메인 요구, 데이터 모델의 제약, 운영 현실>이 만나는 지점에서
도출된 구조입니다.
8. 아직 남아 있는 과제(미해결점)
8.1 테넌트 상속 범위 정의
- 어떤 설정을 상속할 것인가?
- 어떤 문서/정책까지 상속을 허용할 것인가?
8.2 역할 상속 깊이 제한
- override 허용 여부
- 부모 권한 차단 기능 필요성
8.3 ABAC 적용 범위
- 어떤 attribute만 허용할지
- 복잡도 증폭을 방지할 제약 필요
8.4 effectivePermissions 무효화 전략
- 테넌트 이동 시
- 역할 변경 시
- Permission 정책 변경 시 캐시 재계산 규칙
8.5 cross-tenant 참조 정책
- 부모 문서 참조를 허용할 것인가?
- 허용한다면 조건은 무엇인가?
9. 결론: 설계 노트의 의미
이 문서는 완성된 아키텍처 문서가 아닌,
왜 이런 구조를 고민하게 되었는지,
도메인의 본질적 문제를 어떻게 단순화하려 했는지,
아직 남아 있는 과제는 무엇인지를 기록한 메모이자 방향성 문서입니다.
IoT 관제 SaaS에서는 멀티테넌트 구조, 계층형 조직, 권한 모델,
문서 기반 데이터 모델이 필연적으로 요구됩니다.
현재 설계는 이러한 요구를 충족하는 초기 형태이며,
앞으로도 확장 가능성을 고려한 기반을 마련한 상태입니다.
2부. IoT SaaS 데이터 격리 및 권한 모델 설계
이 문서는 단순한 이론 설계가 아니라, 실제 IoT 관제 SaaS를 구축하고 운영하면서
테스트해 본 데이터 격리(multi-tenancy) 및 권한/인가 모델을 정리한 기록입니다.
현재까지의 버전은 다음과 같은 특징을 가집니다.
-
문서(Document) 중심 데이터 모델
- 모든 데이터는
tenantId,schemaName,refId를 가진 독립 문서 단위로 관리됩니다. - 스키마 기반 DTO/Validator를 통해 문서 구조와 의미를 강제합니다.
- 트리 구조가 필요할 때도, 한 문서가 과도하게 비대해지지 않도록 여러 문서로 분할합니다.
- 모든 데이터는
-
테넌트 계층 구조와 디렉터리 비유
- 테넌트는
tenantId,parentTenantId,lineage(/root/brandA/shop001)를 가지며,
PC 파일시스템의 디렉터리처럼 계층 구조를 가집니다. - 이 구조 위에서 상위 테넌트 → 하위 테넌트로의 관제/조회 권한을 줄 수 있도록 설계했습니다.
- 다만, 데이터 자체는 항상 자기
tenantId안에만 속하고,
상/하위 간의 접근 가능 여부는 권한/역할 스코프로만 제어합니다.
- 테넌트는
-
역할(Role) + 권한(Permission) + 스코프(scope) 조합
- Role은 의미 있는 행위 묶음(예: TenantViewer, TenantOperator, DeviceMaintainer)이고,
Permission은action + resourceType + 조건(scope)조합으로 정의합니다. UserRole(userId, roleId, scopeTenantId, scopeType)구조로
“어떤 사용자가, 어떤 테넌트 범위(scope)에서, 어떤 역할을 행사할 수 있는지”를 표현합니다.scopeType = EXACT | WITH_DESCENDANTS를 사용해
상위 테넌트(부모 테넌트)가 하위 테넌트(자식 테넌트)까지 한 번에 관제하는 패턴을 지원합니다.
- Role은 의미 있는 행위 묶음(예: TenantViewer, TenantOperator, DeviceMaintainer)이고,
-
O(1)에 가까운 인가 판단 흐름
- 로그인 시점 또는 테넌트 전환 시점에
effectivePermissions(userId, tenantId)를 계산하고 캐시합니다. - 실제 요청 시에는
userId + tenantId + permissionKey만으로
해당 Permission이 있는지 여부를 바로 검사하는 구조로 구성했습니다. - 이로 인해 테넌트/사용자 수가 늘어나도,
개별 요청의 인가 판단은 거의 O(1)에 가깝게 유지됩니다.
- 로그인 시점 또는 테넌트 전환 시점에
-
IoT 디바이스–테넌트 매핑 일원화
- 각 디바이스는 단일
tenantId에 소속되며,
수집되는 모든 데이터/명령/로그는 수신 시점에tenantId를 확인한 뒤
해당 테넌트 범위에서만 저장/조회되도록 구성했습니다. - 관제 화면에서 테넌트를 전환하면, 그 테넌트에 속한 디바이스와 문서만 쿼리되며,
상위 조직 사용자는WITH_DESCENDANTS스코프로 하위 테넌트들을 한 번에 볼 수 있습니다.
- 각 디바이스는 단일
이 구조는 현재까지 소규모 다중 테넌트 환경 + 제한된 수의 디바이스에서 운영/검증을 진행했습니다.
- 단일 테넌트 수준에서는
- 문서 기반 스키마 구조와 인가 모델이 충분히 단순하고 직관적으로 동작했습니다.
- 부모–자식 테넌트가 섞인 환경에서는
WITH_DESCENDANTS스코프를 통해 상위 조직/하위 조직 관제 시나리오를 구현할 수 있었습니다.- 다만, 역할 상속, cross-tenant 참조, 정책 상속(model inheritance) 등은
아직 “부분적으로만” 사용 중이며, 향후 더 정교한 모델링이 필요한 상태입니다.
이 문서의 나머지 섹션(1–9)은 위에서 소개한 운영 버전을 기준으로
- 어떤 요구사항을 만족시키기 위해
- 어떤 도메인 모델(문서, 스키마, 테넌트, 역할, 권한)을 정의했고
- 어떤 방식으로 인가 서비스와 IoT 관제 시나리오를 설계했는지
를 좀 더 정제된 설계 언어와 구조로 풀어 쓴 내용입니다.
1. 개요 (Overview)
이 문서는 IoT 기반 SaaS 플랫폼에서 데이터 격리(multi-tenancy)와 권한/인가(authorization) 구조를 어떻게 설계하고 구현할 수 있는지를 정리한 기술 문서입니다. 여기서 소개하는 접근 방식은 실제 운영 환경에서 테스트된 경험을 기반으로 하며, 테넌트 구조, 문서 기반 데이터 모델, 역할/권한 모델, IoT 디바이스 매핑 등 서로 다른 도메인을 하나의 일관된 구조로 통합하기 위해 고민한 결과입니다.
특히 다음 세 가지 목표를 중심으로 합니다.
-
고객/조직 단위의 완전한 데이터 분리
각 테넌트는 독립된 데이터 영역을 가지며, 어떤 레벨에서도 다른 테넌트의 데이터가 섞이거나 노출되지 않도록 보장하는 구조를 제공합니다. -
문서(Document) 기반의 유연한 데이터 모델 유지
IoT 디바이스의 상태, 설정, 프로필, 레시피, 사용자 로그 등 다양한 데이터 타입을 모두 문서 단위로 통일해 관리할 수 있게 하며, 스키마(Schema)를 통해 구조적 안정성과 유효성을 보장합니다. -
확장 가능한 권한/인가 모델 구축
테넌트 수와 사용자 수가 증가해도 인가 판단이 부담 없이 작동하도록 구성하며, 다양한 역할(Role)과 권한(Permission)을 조합해 복잡한 SaaS 운영 시나리오를 표현할 수 있습니다.
이를 통해 전체 구조는 단순한 RBAC(Role-Based Access Control)를 넘어서
테넌트 계층 구조, 문서 중심 모델, IoT 디바이스 매핑까지 모두 포함하는
일종의 도메인 아키텍처(Architecture for Domain Isolation & Authorization)를 목표로 합니다.
이 문서의 이후 섹션들은 이러한 목표를 달성하기 위해 마련된
요구사항 → 도메인 모델 → 격리 전략 → 인가 모델 → 운영 시나리오 → 확장성 구조를
순차적으로 설명합니다.
1.5 테넌트(Tenant)의 개념
이 문서에서 사용하는 테넌트(Tenant)라는 용어는
다른 SaaS 서비스에서 흔히 사용하는 단어를 차용한 것으로,
데이터를 조직 단위로 확실하게 분리하기 위해 정의한 논리적 데이터 구획 단위를 의미합니다.
기본적으로 테넌트는 다음과 같은 목적을 가집니다.
- 서로 다른 조직의 데이터가 섞이지 않도록 완전히 분리된 영역을 제공합니다.
- 사용자/기기/문서(Document) 등 모든 데이터 엔티티는 반드시 하나의 테넌트에 속합니다.
- 상위/하위 조직처럼 계층적으로 구성될 수 있으며,
파일시스템의 디렉터리처럼 트리 구조로 표현할 수 있습니다.
테넌트를 파일시스템 디렉터리로 바라본 이유
이 설계에서는 테넌트 구조를 파일시스템 디렉터리 구조와 유사한 개념으로 정의했습니다.
그 배경은 다음과 같습니다.
-
데이터 격리의 명확성
- 디렉터리는 서로 다른 경로의 파일이 섞이지 않는 것처럼,
각 테넌트는 다른 테넌트의 데이터와 절대 섞이지 않습니다.
- 디렉터리는 서로 다른 경로의 파일이 섞이지 않는 것처럼,
-
확장성과 직관성
- 조직이 상위/하위 구조를 가진다면,
/root/organizationA/branch001형태로 경로(path) 기반 트리로 표현하는 것이
구조적 일관성과 직관성을 모두 충족합니다.
- 조직이 상위/하위 구조를 가진다면,
-
정책/권한 상속 모델을 단순하게 표현 가능
- 디렉터리처럼 트리를 기반으로 하면
“상위 조직이 하위 조직을 관제할 수 있다”는 정책을
WITH_DESCENDANTS같은 스코프(scope)로 간단히 표현할 수 있습니다.
- 디렉터리처럼 트리를 기반으로 하면
테넌트가 포함하는 정보
테넌트는 다음 속성들을 가진 엔티티입니다.
tenantId: 테넌트를 식별하는 고유 IDparentTenantId: 상위 테넌트 IDlineage: 루트부터 현재 테넌트까지의 경로 (예:/root/orga/sub1)name,type,status등 기본 메타데이터
테넌트가 이 문서에서 중요한 이유
이 문서의 대부분의 설계는 다음과 같은 원칙에서 출발합니다.
- 문서(Document), 사용자(User), 기기(Device), 로그(Log) 등
모든 데이터 모델은 반드시 하나의 테넌트에 속해야 합니다. - 데이터 접근 권한(Authorization)도 항상
“어떤 테넌트의 데이터에 접근하려는가?”라는 컨텍스트를 기준으로 판단합니다. - 상위 조직이 하위 조직의 데이터를 관리할 수 있는가?는
오직 역할(Role)과 스코프(scope)에 의해 제어됩니다.
따라서 테넌트는
이 문서 전체의 설계에서 데이터 격리의 기본 단위이자 권한 모델의 핵심 축으로 사용됩니다.
2. 요구사항 정리
IoT 관제 SaaS에서 데이터 격리와 권한/인가 모델을 설계하기 위해 먼저 충족해야 할 요구사항을 기능적 측면과 비기능적 측면으로 나누어 정의합니다. 이 요구사항들은 이후 도메인 모델과 인가 구조의 설계 기준이 됩니다.
2.1 기능 요구사항
1) 테넌트(Tenant) 단위의 독립적 관리
- 각 조직은 독립된 테넌트로 관리되어야 합니다.
- 서로 다른 테넌트의 데이터는 기본적으로 완전히 분리되어야 합니다.
2) 사용자–테넌트–역할 구조
- 사용자는 하나 이상의 테넌트와 관계를 가질 수 있어야 합니다.
- 사용자에게는 테넌트별로 역할(Role)이 할당됩니다.
- 역할은 문서(Document), 기기(Device), 설정(Configuration) 등 특정 자원(Resource)에 대한 접근 권한을 정의합니다.
3) 문서(Document) 접근 제어
- 사용자는 자신의 역할에 따라 특정 문서 스키마를 읽기/쓰기/관리할 수 있어야 합니다.
- 권한은 스키마 단위, 문서 유형 단위, 특정 Resource 단위로 세분화할 수 있어야 합니다.
4) 기기(Device) 단위 접근 제어
- 사용자는 특정 기기 또는 기기 그룹에 한정된 접근 권한을 가질 수 있어야 합니다.
- 기기 데이터(로그, 상태, 명령)는 반드시 해당 기기가 속한 테넌트의 범위 내에서만 접근 가능해야 합니다.
5) 상위/하위 조직 구조 지원
- 상위 조직은 필요한 경우 하위 조직의 상태를 관제할 수 있어야 합니다.
- 단, 접근 가능 여부는 테넌트의 상/하 관계 그 자체가 아니라 역할(Role)과 스코프(scope)로 제어되어야 합니다.
- 기본 원칙:
- 하위 조직은 상위 조직 데이터를 기본적으로 조회할 수 없습니다.
- 상위 조직은 허용된 범위(scope)가 부여된 경우에만 하위 조직을 관제할 수 있습니다.
2.2 비기능 요구사항
1) 인가 판단 성능
- 인가 판단은 요청당 O(1)에 가까운 시간으로 처리되어야 합니다.
- 많은 테넌트와 사용자, 다양한 역할이 존재해도 성능 저하가 없어야 합니다.
2) 확장성(Scalability)
- 테넌트 수, 사용자 수, 디바이스 수가 증가해도 인가 서비스의 부하가 선형적으로만 증가해야 합니다.
- 분산 환경에서도 인가 캐시 및 테넌트 구조 조회가 효율적으로 동작해야 합니다.
3) 모델 변경의 안정성
- 스키마, 권한 구조, 테넌트 계층이 바뀌더라도 안전하게 확장될 수 있어야 합니다.
- 문서 스키마 변경은 버전 관리 체계를 갖추고, 기존 데이터의 자동/수동 마이그레이션 절차를 지원해야 합니다.
4) IoT 데이터의 정확한 테넌트 라우팅
- IoT 기기에서 들어오는 데이터(로그, 상태, 명령)는 프로토콜 레벨에서 테넌트 ID를 확실히 구분해야 합니다.
- 잘못된 테넌트로 데이터가 섞이는 상황이 절대 발생하지 않도록 방어적인 설계를 갖춰야 합니다.
3. 도메인 모델
IoT SaaS 데이터 격리 구조는 문서(Document), 스키마(Schema), 테넌트(Tenant), 사용자(User), 역할(Role), 권한(Permission) 등 여러 엔티티가 상호작용하면서 구성됩니다. 이 섹션에서는 전체 구조를 이루는 핵심 도메인 모델을 정의하고, 각 모델이 데이터 격리 및 인가 판단에 어떻게 기여하는지 설명합니다.
3.1 문서(Document) 모델
이 설계에서 대부분의 데이터는 문서(Document)라는 단위로 저장됩니다. 문서는 IoT 장비 상태, 레시피, 프로필, 사용자 로그, 설정 등 다양한 데이터를 하나의 통합된 구조로 수용하는 역할을 합니다.
문서의 공통 속성
모든 문서는 공통적으로 다음 속성을 가집니다.
-
tenantId
문서가 속한 테넌트. 데이터 격리의 1차 기준입니다. -
schemaName
문서의 구조와 의미를 결정하는 스키마의 이름입니다. -
refId
문서를 전역적으로 식별할 수 있는 고유 참조 ID입니다. -
payload
문서의 실제 데이터입니다. 스키마(DTO/Validator)에 의해 검증됩니다. -
meta
생성자, 생성 시각, 수정 이력, 버전 등 문서에 대한 부가 정보입니다.
설계 원칙
-
스키마가 존재해야만 문서를 생성할 수 있습니다.
임의의 구조를 가진 문서는 허용하지 않습니다. -
문서 간 참조는
(schemaName, refId)로만 이루어집니다.
경로 기반, 트리 기반 참조 등은 허용하지 않으며,
문서 간 의존성은 모두 스키마 단위의 명시적 참조로 관리됩니다. -
트리 구조는 필요할 때 문서를 분할해 구성합니다.
한 문서 안에 지나치게 깊은 트리를 넣지 않고,
여러 문서로 구성된 느슨한 트리 구조를 사용합니다.
이 모델을 통해 데이터는 강하게 구조화되면서도 충분히 유연함을 유지할 수 있습니다.
3.2 스키마(Schema)와 DTO
스키마는 문서의 구조(Structure), 데이터 타입, 의미적 제약조건을 정의하는 계약(Contract)입니다.
모든 문서는 해당 스키마에서 정의한 규칙에 따라 생성/수정/검증됩니다.
스키마의 구성 요소
각 스키마는 다음 요소들로 구성됩니다.
-
DTO 정의
문서가 가져야 하는 필드와 데이터 타입을 명세합니다. -
Generator(생성기)
새 문서를 만들 때 필요한 필드 채움 규칙을 정의합니다. -
Validator(밸리데이터)
문서 저장/수정 시 데이터가 올바른지 검증합니다.
스키마 변경과 버전 관리
스키마는 시간이 지남에 따라 변경될 수 있습니다.
스키마 변경 시 다음 원칙을 따릅니다.
- 새로운 버전의 스키마는 기존 문서와 충돌하지 않도록
schemaName + version조합으로 관리됩니다. - 기존 문서를 새 버전에 맞게 자동 또는 수동으로 마이그레이션할 수 있습니다.
- 하위 호환이 필요 없는 구조에서는 스키마 버전이 명확하게 분기됩니다.
3.3 테넌트(Tenant)와 계층 구조
테넌트는 조직 단위 데이터 격리의 기본 단위이며,
파일시스템의 디렉터리처럼 계층 트리 구조로 확장될 수 있습니다.
테넌트의 핵심 속성
-
tenantId
각 테넌트를 식별하는 고유 ID입니다. -
parentTenantId
상위 테넌트의 ID입니다. 루트 테넌트는null을 가집니다. -
lineage또는path
루트부터 현재 테넌트까지의 경로입니다.
예:/root/orga/sub1/unit3 -
이름, 타입, 상태 등 메타데이터.
데이터 격리 원칙
-
tenantId가 다르면 데이터는 항상 분리됩니다.
문서, 기기 정보, 설정, 로그 등 모든 데이터가 포함됩니다. -
부모–자식 관계는 데이터 접근을 자동으로 허용하지 않습니다.
접근 여부는 항상 역할(Role) + 스코프(scope) 규칙으로 제어합니다. -
cross-tenant 참조는 명시적으로만 허용됩니다.
예: 상위 조직이 공통 프로필 문서를 read-only로 하위 조직에 제공하는 경우.
이 구조는 단순한 격리 모델을 유지하면서도
계층 구조에 기반한 관제 기능을 유연하게 추가할 수 있게 합니다.
3.4 사용자(User), 역할(Role), 권한(Permission)
권한/인가 모델의 중심에는 사용자(User), 역할(Role), 권한(Permission)의 관계가 있습니다.
사용자(User)
- 전역적으로 하나의
userId를 가집니다. - 하나 이상의 테넌트와 관계를 가집니다.
- 테넌트마다 서로 다른 역할(Role)을 가질 수 있습니다.
역할(Role)
역할은 “어떤 행동을 할 수 있는지”를 묶어놓은 단위입니다.
예시:
-
TenantViewer
지정된 테넌트에서 문서 조회만 가능합니다. -
TenantOperator
설정 변경, 기기 제어 등 일부 쓰기 권한을 포함합니다. -
DeviceMaintainer
특정 기기 그룹에 대한 유지보수 권한을 가집니다.
권한(Permission)
권한은 다음 요소로 정의됩니다.
action(READ, WRITE, EXECUTE 등)resourceType(DOCUMENT, DEVICE, CONFIG 등)조건(scope)(스키마명, 기기 그룹, 특정 refId 등)
예시:
DOCUMENT:READ:SCHEMA=BREW_PROFILEDEVICE:COMMAND:SCOPE=OWNED_BY_TENANT
역할–권한 관계
- 각 역할(Role)은 여러 Permission을 포함합니다.
- 역할은 선택적으로 부모 역할을 가질 수 있습니다.
이를 통해 상속 기반 역할 확장을 표현할 수 있습니다. - 최종적으로 사용자가 갖는 권한 집합은
역할(Role)과 그 부모 역할의 Permission을 합성한 결과입니다.
사용자–역할(UserRole) 할당
사용자에게 역할을 부여할 때는 다음 형태로 저장합니다.
userIdroleIdscopeTenantIdscopeTypeEXACT: 해당 테넌트에서만 유효WITH_DESCENDANTS: 해당 테넌트와 모든 하위 조직에서 유효
이 구조를 통해 같은 사용자라도
조직별/범위별로 다른 권한을 가질 수 있습니다.
4. 데이터 격리 전략
멀티테넌시 기반의 IoT SaaS에서 가장 중요한 목표는 서로 다른 조직의 데이터가 절대로 섞이지 않도록 보장하는 것입니다. 이 섹션에서는 테넌트(Tenant) 단위 격리를 중심으로, 문서 간 참조 규칙과 기기/로그/상태 데이터가 테넌트별로 안전하게 저장되도록 하는 전략을 정의합니다.
4.1 테넌트 단위 격리
데이터 격리의 핵심 원칙은 다음과 같습니다.
1) 모든 데이터는 반드시 하나의 테넌트에 속한다
- IoT 기기 데이터(상태, 로그)
- 문서(Document)
- 설정(Configuration)
- 프로필/레시피
- 사용자 로그(User Event Log)
이 모든 데이터 엔티티는 생성 시 반드시 tenantId를 포함해야 합니다.
2) IoT 기기–테넌트 매핑은 단일 원칙을 따른다
각 디바이스는 단일 tenantId에 속합니다.
기기가 전송하는 모든 데이터는 프로토콜 단에서 다음 과정을 거칩니다.
- 수신된 패킷에 포함된 tenantId를 확인
- 유효한 tenantId인지 검증
- 해당 tenantId의 데이터 영역에만 저장
이 흐름을 통해 기기에서 잘못된 테넌트로 데이터가 유입되는 상황을 원천 차단합니다.
3) 저장소 선택: 논리적 격리 vs 물리적 격리
테넌트 기반 데이터 분리는 저장 방식에 따라 두 가지 시나리오로 확장할 수 있습니다.
-
단일 DB 내 논리적 격리
모든 테넌트의 데이터를 같은 DB에 저장하되,tenantId를 기준으로 구분합니다. -
테넌트별 물리적 DB 또는 스키마 분리
대형 고객 또는 규제 요구가 있는 경우
테넌트마다 독립된 스키마 / DB / 인스턴스를 제공할 수 있습니다.
이 문서에서 설명하는 모델은 논리적 격리를 기본으로 하되,
물리적 분리로 확장될 수 있도록 테넌트 ID 기반 구조를 일관되게 유지합니다.
4.2 문서 간 참조 규칙
문서(Document) 기반 데이터 모델을 사용할 때,
문서 A가 문서 B를 참조하는 상황은 자주 발생합니다.
이때 데이터 격리를 해치지 않도록 다음 규칙을 따릅니다.
1) 기본 규칙: 동일 테넌트 간 참조만 허용
- 문서 A와 문서 B가 서로 참조하려면
두 문서의tenantId는 반드시 같아야 합니다. - 이는 스키마 수준에서 강제할 수 있으며,
cross-tenant 참조는 기본적으로 금지됩니다.
2) 예외: 상위/하위 조직 구조에서의 제한적 참조
운영을 위해 상위 조직이 하위 조직에게
공통 문서(예: 공통 레시피, 공통 기기 설정)를 제공해야 하는 경우가 있습니다.
이 경우 다음 조건에서 명시적으로 허용합니다.
- 스키마 차원에서 “cross-tenant read-only allowed” 규칙을 선언
- 참조 가능한 방향은 상위 조직 → 하위 조직으로 제한
- 읽기 권한만 허용하며, 수정/삭제는 금지
이를 통해 격리를 유지하면서도 운영 측면의 유연성을 확보할 수 있습니다.
3) 참조 방식은 항상 (schemaName, refId) 조합
경로(Path), 조합키, 내부 트리 구조 같은 표현은 사용하지 않습니다.
- 문서 간 참조는 반드시
schemaName+refId조합으로 표현해야 합니다. - 이 방식은 문서 모델을 독립적이고 재사용 가능하게 유지할 수 있습니다.
5. 권한 및 인가 모델
테넌트 단위 데이터 격리를 완성하기 위해서는
데이터 접근을 허용할지 여부를 판단하는 인가(Authorization) 구조가 반드시 필요합니다.
이 섹션에서는 사용자(User), 역할(Role), 권한(Permission), 스코프(scope) 개념을 기반으로
용량이 증가하더라도 안정적으로 동작하는 인가 모델을 정의합니다.
5.1 기본 원칙
인가 판단은 다음 세 가지 요소를 기준으로 이루어집니다.
-
userId
요청을 수행하는 사용자 식별자 -
tenantId (contextTenantId)
사용자가 지금 접근하려는 테넌트 컨텍스트
예: 관제 화면에서 사용자가 선택한 하위 조직 테넌트 -
permissionKey
수행하려는 행동을 식별하는 권한 키
예:DOCUMENT:READ:SCHEMA=BREW_PROFILEDEVICE:COMMAND:SCOPE=OWNED_BY_TENANT
테넌트 기반 접근 원칙
-
사용자는 특정 테넌트의 데이터를 조회/수정하기 위해
그 테넌트 또는 해당 테넌트의 상위 조직 범위에서 부여된 역할(Role)을 가져야 합니다. -
상위 조직 → 하위 조직 관제는 허용 가능하지만
하위 조직 → 상위 조직 접근은 기본적으로 금지됩니다. -
조직 간 접근 가능 여부는
테넌트 계층 관계가 아니라 역할(Role)과 스코프(scope)를 통해 제어합니다.
스코프(Scope)의 핵심 개념
역할(Role)을 부여할 때
사용자가 어떤 테넌트 범위에서 그 역할을 행사할 수 있는지 정의합니다.
-
EXACT
지정된 테넌트에서만 유효 -
WITH_DESCENDANTS
지정된 테넌트와 그 하위 조직 전체에서 유효
이 구조를 통해 다음과 같은 시나리오를 간단히 지원할 수 있습니다.
- 상위 조직 관리자가 하위 조직 전체를 관제
- 특정 지점/조직만 관리하는 제한된 역할 부여
- 테넌트 계층이 확장되더라도 권한 모델을 재설계할 필요 없음
5.2 Effective Permissions 계산
인가 요청을 O(1)에 가깝게 처리하기 위해
사용자의 역할(Role)과 권한(Permission)을 사전에 계산해 두는 방식을 사용합니다.
Effective Permissions란?
사용자가 특정 테넌트의 컨텍스트에서
실제로 행사할 수 있는 최종 권한(permission)들의 집합을 의미합니다.
예시:
사용자 U가 테넌트 T에 대해
TenantOperator역할을 EXACT 스코프로 가지고 있고TenantViewer역할을 상위 조직에서 WITH_DESCENDANTS로 부여받고 있다면
두 역할과 그 부모 역할의 Permission을 합성해
U가 T에서 사용할 수 있는 실제 권한 집합이 됩니다.
계산 규칙
Effective Permissions는 다음 조건의 UserRole들을 합성해 계산합니다.
-
scopeType = EXACT
→scopeTenantId === contextTenantId인 경우 포함 -
scopeType = WITH_DESCENDANTS
→contextTenantId가scopeTenantId의 하위 조직인 경우 포함 -
각 UserRole이 가진 역할(Role)에 포함된 Permission들을 수집
-
역할이 부모 역할을 가진 경우
부모 역할들의 Permission도 포함해 평탄화(flatten)합니다.
언제 계산하나?
- 로그인 시점
- 사용자가 테넌트를 전환하는 시점
- Role/Permission 구조가 변경되어 캐시를 무효화해야 하는 시점
실제 요청 처리 흐름
-
클라이언트/서버는 헤더 또는 컨텍스트에서
userId,tenantId를 식별합니다. -
인가 서비스는 캐시에 저장된
effectivePermissions(userId, tenantId)를 조회합니다. -
요청에 필요한
permissionKey가
해당 Permission 집합에 포함되어 있는지 확인합니다. -
포함되어 있으면 허용(Allow),
포함되어 있지 않으면 거절(Deny)합니다.
성능적 이점
- 권한 판단은 단순한 Set 포함 여부 검사로 처리됩니다.
- 따라서 요청당 인가 비용은 O(1)에 가깝게 유지됩니다.
- 테넌트 수와 사용자 수가 증가해도
인가 서비스의 부하는 비례적으로만 증가합니다.
6. 인가 서비스 설계
권한 및 데이터 격리 모델을 실제 시스템에서 안정적으로 운용하기 위해서는
이를 책임지는 인가(Authorization) 서비스가 필요합니다.
이 서비스는 사용자–테넌트–역할 관계를 관리하고,
요청 시 권한 판단을 빠르게 수행하는 핵심 컴포넌트입니다.
6.1 역할(Role)과 책임
인가 서비스는 다음 기능을 수행합니다.
1) 사용자–테넌트–역할(UserRole) 관계 조회
- 특정 사용자(userId)가 어떤 테넌트(scopeTenantId)에
어떤 역할(roleId)을 가지고 있는지 조회합니다. - 스코프(scopeType)가 EXACT인지 WITH_DESCENDANTS인지 판별합니다.
2) Role / Permission 정의 관리
- 역할(Role)의 생성, 수정, 삭제
- 역할에 포함된 Permission 집합 관리
- (선택적으로) 역할 간 부모–자식 관계 설정
3) 테넌트 계층 기반 스코프 검증
WITH_DESCENDANTS역할의 유효성을 판단하기 위해
특정 테넌트가 다른 테넌트의 하위 조직인지 빠르게 판별해야 합니다.- 이를 위해 테넌트 엔티티의 lineage(path) 또는 precomputed ancestry 정보를 사용합니다.
4) Effective Permissions 계산 및 캐싱
- 특정
userId + tenantId조합에 대해
실제 사용 가능한 Permission 집합을 계산합니다. - 이 결과를 캐싱하여 요청당 인가 판단을 O(1)로 수행합니다.
5) 외부 서비스의 인가 요청 처리
웹 앱, API 서버, IoT Gateway 등 외부 컴포넌트는
인가 서비스에 다음과 같은 질의를 수행합니다.
- “이 사용자가 이 테넌트에서 이 권한을 가지고 있는가?”
- “이 사용자가 이 테넌트에서 가진 모든 권한을 알려 달라.”
6.2 주요 API 예시
인가 서비스는 다음과 같은 API(Sync 또는 gRPC/RPC)를 제공할 수 있습니다.
POST /authz/evaluate
특정 권한의 허용 여부를 판단합니다.
입력
{
"userId": "user-123",
"tenantId": "tenant-A1",
"permissionKey": "DOCUMENT:READ:SCHEMA=BREW_PROFILE"
}
출력
{ "allow": true }
GET /authz/effective-permissions
특정 테넌트 컨텍스트에서
사용자가 실제로 행사할 수 있는 Permission 집합을 반환합니다.
입력 예시
/authz/effective-permissions?userId=user-123&tenantId=tenant-A1
출력
{
"permissions": [
"DOCUMENT:READ:SCHEMA=BREW_PROFILE",
"DOCUMENT:WRITE:SCHEMA=DEVICE_CONFIG",
"DEVICE:COMMAND:SCOPE=OWNED_BY_TENANT"
]
}
POST /authz/roles
새로운 Role을 생성하거나 기존 Role을 수정합니다.
입력 예시
{
"roleId": "TenantOperator",
"permissions": [
"DOCUMENT:READ:SCHEMA=BREW_PROFILE",
"DOCUMENT:WRITE:SCHEMA=BREW_PROFILE",
"DEVICE:COMMAND:SCOPE=OWNED_BY_TENANT"
],
"parentRoleId": "TenantViewer"
}
POST /authz/user-roles
특정 사용자에게 Role을 부여합니다.
입력 예시
{
"userId": "user-123",
"roleId": "TenantOperator",
"scopeTenantId": "tenant-A1",
"scopeType": "WITH_DESCENDANTS"
}
이 API는 상위 조직이 하위 조직까지 관제할 수 있도록 하는 핵심 요소입니다.
6.3 캐시 전략
인가 판단의 성능은 전체 시스템의 응답 속도와 확장성에 직접적인 영향을 미칩니다.
따라서 effectivePermissions(userId, tenantId) 결과를 어떻게 캐싱할지 명확한 전략이 필요합니다.
1) 캐시 저장 위치
효율적인 인가 판단을 위해 캐시는 다음 두 가지 방식 중 하나 또는 둘 다 사용합니다.
-
인가 서비스 인메모리 캐시
가장 빠른 조회가 가능합니다. 단일 인스턴스 환경 또는 로컬 부하가 높은 환경에 적합합니다. -
Redis 등 외부 캐시
여러 인가 서비스 인스턴스를 운영할 때 권장됩니다.
스케일 아웃 시 캐시 일관성과 분산 처리가 쉬워집니다.
2) 캐시 키 구조
캐시 키는 사용자와 테넌트 컨텍스트 조합으로 구성합니다.
effective:{userId}:{tenantId}
예:
effective:user-123:tenant-A1
이 구조는 서로 다른 테넌트 컨텍스트에서
동일 사용자가 다른 권한 세트를 가질 수 있는 상황을 명확하게 분리해 줍니다.
3) 캐시 무효화(Invalidation)
권한/역할/테넌트 정보가 변경되면
기존 캐시 결과가 더 이상 유효하지 않으므로 반드시 무효화해야 합니다.
다음 변경 이벤트는 캐시 무효화가 필요합니다.
-
UserRole 변경
- 특정 사용자에게 역할이 추가되거나 제거되는 경우
- 역할의 스코프(scopeTenantId, scopeType)가 변경되는 경우
-
Role 변경
- 역할 자체의 Permission 집합이 수정되는 경우
- 역할 간 부모–자식 구조가 변경되는 경우
-
테넌트 계층 변경
parentTenantId가 변경되거나 테넌트 이동/병합이 발생하는 경우WITH_DESCENDANTS스코프의 유효성 판단이 달라지는 상황
캐시 무효화 방식은 다음 중 하나를 선택합니다.
- 해당 사용자–테넌트 조합의 캐시만 무효화
- 해당 사용자 관련 모든 effective 캐시 무효화
- (확장 환경) 관련 테넌트 라인의 모든 사용자 캐시 무효화
4) 버전(Version) 기반 캐시 검증
대규모 환경에서는 캐시 무효화를 전파하기 위해
버전 기반 접근 방식을 적용할 수 있습니다.
권한 버전(permissionVersion) 전략
- 사용자 또는 역할(Role) 단위로
permissionVersion을 관리합니다. - 캐시에는 해당 버전 정보를 함께 저장합니다.
- 인가 요청 시 현재 버전과 캐시 버전이 다르면
캐시를 폐기하고 Effective Permissions를 재계산합니다.
이 방식의 장점은 다음과 같습니다.
- 전체 캐시를 폭발적으로 무효화할 필요가 없습니다.
- 변경된 사용자/역할만 국소적으로 무효화됩니다.
5) TTL(Time-to-Live) 옵션
선택적으로 TTL을 둘 수 있습니다.
- 짧은 TTL: 변경 전파가 느슨한 환경에서 안전
- 긴 TTL 또는 무TTL: 변경이 빈번하지 않은 환경에서 성능 최적화에 유리
본 문서에서는 명시적 무효화 + 버전 검증 조합을 우선 전략으로 합니다.
7. IoT 관제 시나리오 예시
지금까지 정의한 테넌트 구조, 문서 모델, 권한/인가 모델이
IoT 디바이스 운영 환경에서 실제로 어떻게 동작하는지를
세 가지 대표적인 시나리오를 통해 설명합니다.
7.1 기기–테넌트 매핑
각 IoT 디바이스(deviceId)는 반드시 하나의 테넌트에 속합니다.
이는 데이터 격리의 핵심 전제이며, 다음 원칙에 따라 동작합니다.
1) 디바이스 등록 시 테넌트 지정
디바이스는 등록 과정에서 다음 정보를 가집니다.
deviceIdtenantId- 장비 모델/시리얼/설치 위치 등의 메타데이터
디바이스가 네트워크에 연결되거나 데이터를 전송할 때
서버는 패킷에 포함된 tenantId를 검증하여
올바른 테넌트에 속한 장비인지 확인합니다.
2) 데이터 수신 시 테넌트 검증
디바이스에서 전송되는 다음 데이터들은 모두
수신 시점에 tenantId를 기준으로 검증/저장됩니다.
- 실시간 상태 보고
- 센서 데이터
- 에러/로그 데이터
- 명령 응답 등
서버는 tenantId를 기준으로 저장 위치를 결정하며,
다른 테넌트의 데이터 영역에는 절대로 기록되지 않습니다.
3) 잘못된 테넌트 데이터 방지
프로토콜 레벨에서 tenantId를 명시적으로 포함시키면
기기 오동작이나 잘못된 라우팅으로 인해
타 테넌트 데이터가 오염되는 상황을 방어할 수 있습니다.
7.2 관제 화면에서의 흐름
사용자가 웹 앱/모바일 앱 등에서
어떤 테넌트(조직)를 선택했는지에 따라 관제 화면의 모든 정보가 결정됩니다.
전체 흐름
-
사용자 로그인
- 사용자
userId를 기준으로
접근 가능한 테넌트 목록을 조회합니다.
- 사용자
-
사용자 선택에 따라 특정 테넌트 컨텍스트 활성화
- 예: 사용자 A는
tenant-X1,tenant-X2두 곳에 접근 가능 - 사용자가 관제 UI에서
tenant-X2를 선택하면
contextTenantId = tenant-X2가 됩니다.
- 예: 사용자 A는
-
Effective Permissions 계산/로드
- 로그인 또는 테넌트 선택 시
effectivePermissions(userId, contextTenantId)를 캐시에서 조회합니다. - 캐시가 없거나 무효화된 경우 계산하여 캐시에 저장합니다.
- 로그인 또는 테넌트 선택 시
-
인가 기반 UI 렌더링
- 읽기 권한이 없는 문서는 목록에서 제외
- 쓰기 권한이 없는 설정 페이지는 비활성화
- 특정 기기 제어 버튼은 권한이 있을 때만 노출
-
데이터 조회
- 선택된 테넌트에 속한 장비 목록, 레시피, 프로필, 로그 등을 쿼리합니다.
- 이때 서버는
userId가 아니라contextTenantId를 기준으로 데이터를 반환합니다.
-
요청별 인가 처리
- 사용자가 특정 문서를 열거나 수정할 때
permissionKey를 확인하여 Allow/Deny를 즉시 판단합니다.
- 사용자가 특정 문서를 열거나 수정할 때
7.3 상위 조직의 하위 조직 관제
이 구조는 상위 조직이 하위 조직 전체를 한 번에 관제하는 시나리오를 지원합니다.
예: 상위 조직 사용자가 하위 조직 전체를 조회하는 흐름
-
상위 조직의 사용자 U는 다음 역할을 가집니다.
roleId = TenantOperatorscopeTenantId = 상위조직scopeType = WITH_DESCENDANTS
-
사용자 U는 UI에서 특정 하위 조직 테넌트를 선택할 수 있습니다.
- 선택한 시점에
contextTenantId가 해당 하위 조직으로 전환됩니다.
- 선택한 시점에
-
인가 서비스는 다음을 판단합니다.
scopeType = WITH_DESCENDANTS- 상위 조직이 선택된 하위 조직의 조상인지 여부
→lineage또는parentTenantId트리 구조로 검증
-
조건이 충족되면
해당 하위 조직에 속한 문서/기기/로그를 조회할 수 있습니다.
접근 방향 규칙
-
상위 → 하위 조직
→WITH_DESCENDANTS스코프가 있는 경우에만 가능 -
하위 → 상위 조직
→ 기본적으로 불가능
→ 명확한 예외 정책(예: 상위 조직의 공통 정책 문서 read-only 제공)이 없는 한 허용하지 않습니다.
장점
- 계층 구조가 깊어져도 권한 모델은 단순하게 유지됩니다.
- 조직 개편이나 트리 재구성이 발생해도
스코프 기반 인가 모델은 구조적 안정성을 유지합니다.
8. 버전 관리 및 확장성
멀티테넌트 IoT SaaS에서 데이터 모델과 권한 구조는
시간이 지남에 따라 변경될 가능성이 높습니다.
스키마, 역할, 테넌트 구조가 발전하더라도
기존 데이터와 인가 모델을 안전하게 유지할 수 있어야 합니다.
이 섹션에서는 장기 운영을 위한 버전 관리 전략과
서비스가 성장할 때 고려해야 하는 확장성 구조를 설명합니다.
8.1 스키마 버전 관리
문서(Document)의 스키마는 지속적으로 확장/변경될 수 있습니다.
기존 데이터와 호환성을 유지하면서 변화에 대응하기 위해 다음 원칙을 따릅니다.
1) 스키마는 schemaName + schemaVersion 조합으로 관리
- 동일한 기능 그룹(document type)의 스키마라도,
새로운 구조나 필드를 추가할 때는 버전 번호를 증가시킵니다. - 예:
BREW_PROFILE:v1BREW_PROFILE:v2
2) 문서에는 생성 당시 스키마 버전을 기록
meta.schemaVersion으로 문서가 어떤 스키마 버전으로 생성되었는지 명확히 표시합니다.
3) 스키마 변경 시 마이그레이션 정책 수립
- 자동 변환이 가능한 경우: 시스템 내 변환기(Transformer) 적용
- 자동 변환이 불가능한 경우: 운영 도중 점진적 수동 마이그레이션
- 데이터 양이 많은 스키마는 교체 방식(신규 스키마 도입 후 gradual migration)을 선택
이렇게 하면 스키마 변경이 잦더라도 시스템 일관성을 유지할 수 있습니다.
8.2 Role / Permission 버전 관리
역할(Role)과 권한(Permission)도 변경될 수 있습니다.
특정 기능이 삭제되거나 새로운 기능이 추가되면
Permission 정의와 역할 구성이 바뀔 수 있습니다.
권장 방식
- 기존 Role을 수정하기보다는 새로운 Role 정의를 추가
- 기존 Permission을 수정하기보다는 새 PermissionKey를 추가
- 사용자(UserRole)에게 할당된 Role을 점진적으로 재할당
- 기존 Role은 deprecated 처리 후 일정 기간 뒤 삭제
이 방식은 운영 중 “역할 충돌”이나 “권한 오류”를 방지합니다.
8.3 테넌트 계층 구조 확장
운영 중 조직 구조(테넌트 트리)가 다음과 같이 변화할 수 있습니다.
- 새로운 조직/지점 추가
- 조직 간 병합
- 상위/하위 조직 관계 변경
- 다단계 트리 심화
이러한 변동이 발생하더라도 다음 원칙을 유지합니다.
1) 테넌트의 lineage(path)를 항상 최신 상태로 유지
조직 이동 또는 병합이 발생할 때
parentTenantIdlineage (/root/...)
를 일관성 있게 재생성합니다.
2) WITH_DESCENDANTS 스코프는 lineage 기반으로 자동 재해석
테넌트 계층 구조가 변경되면
스코프(scope)의 유효 범위도 lineage 변화에 맞게 자동 조정됩니다.
3) 캐시 무효화
테넌트 이동/병합은 캐시된 Effective Permissions 결과를 무효화해야 합니다.
(섹션 6.3에서 설명한 전략 활용)
8.4 확장성 고려 사항
멀티테넌트 SaaS가 성장하면서 테넌트 수/사용자 수/기기 수가 크게 증가하더라도
인가 판단과 데이터 접근이 안정적으로 동작해야 합니다.
이 구조는 다음과 같은 이유로 대규모 환경에서도 확장성을 확보할 수 있습니다.
1) 대규모 테넌트 수
테넌트 계층이 깊어지거나 수백/수천 개로 늘어나더라도
조직 관계 판별(상위/하위 조직 여부)은 lineage 기반 prefix 비교만 수행하면 됩니다.
- 테넌트의 경로는
/root/orgA/sub1같은 문자열 lineage로 표현됩니다. - 상위 조직 여부 판단은
lineageA가lineageB의 접두사(prefix)인지 비교하는 방식으로 이루어집니다. - 문자열 비교 연산은 일반적으로 매우 빠르며,
경로의 depth가 작기 때문에 O(1)에 가깝거나 O(depth)로 처리됩니다.
즉, 테넌트 수가 증가해도 lineage 계산의 비용은 미미합니다.
2) 대규모 사용자 수
인가 요청당 필요한 연산은 다음과 같습니다.
- Effective Permissions 계산은 로그인 또는 테넌트 전환 시 1회 수행
- 요청당 인가 판단은 O(1)
- 캐시 기반 구조를 통해 고부하 환경에서도 안정적입니다.
3) 대규모 IoT 디바이스 수
- 기기–테넌트 매핑은 단일
tenantId기준으로 잘 분리되므로
데이터 혼합 없이 안전하게 확장 가능합니다. - 테넌트 단위 쿼리만 수행하므로 DB 스캔 범위가 크지 않습니다.
4) 물리적 격리로 확장 가능
- 초기에는 논리적 격리(
tenantId컬럼 기반)로 시작하되,
대규모 고객 또는 보안 규제 요구가 있으면
테넌트 단위 DB/스키마로 분리할 수 있습니다. - 문서 모델, 인가 모델은 그대로 유지되므로 전환 비용이 낮습니다.
8.5 장기 운영을 위한 설계 원칙 요약
- 추상화 우선: 스키마, 문서, 역할 구조는 추상화 레이어를 유지
- 분리된 책임: 테넌트 격리/인가/IoT 데이터/UI는 분리된 구조로 개발
- 확장성 중심: 계층 구조, 권한 구조, 문서 구조는 언제든 확장 가능하도록 설계
- 안전한 변경: 버전 관리와 캐시 무효화를 명확히 정의하여 운영 혼란 방지
- 정책 중심: 상위/하위 조직 접근은 구조가 아니라 정책(스코프)으로 제어
9. 운영 및 모니터링
멀티테넌트 IoT SaaS에서 데이터 격리와 인가 모델이 안정적으로 동작하려면
운영 과정에서 시스템이 올바르게 동작하고 있는지 지속적으로 관찰하고 검증해야 합니다.
이 섹션은 운영 중 모니터링해야 할 핵심 지표, 로깅 전략, 장애 대응 관점에서의 고려사항을 정리합니다.
9.1 인가 실패 로그 관리
인가 실패는 잘못된 접근, 설정 오류, 역할 오할당, 테넌트 경계 문제를 식별하는 데 중요한 지표입니다.
1) 인가 실패 시 기록해야 할 정보
userIdcontextTenantId(사용자가 접근하려 한 테넌트)permissionKey- 실패 유형(권한 없음, 스코프 미충족, 문서 스키마 제한 등)
- 요청 URL, HTTP 메서드
- 요청 시점(timestamp)
이 정보를 기록하면 운영자가 다음을 정확하게 파악할 수 있습니다.
- 어떤 사용자에게 어떤 권한이 부족했는지
- 역할(Roles)의 설정 문제가 있는지
- 테넌트 구조가 바뀌었는데 캐시가 최신이 아닌 경우인지
- IoT 기기나 앱에서 잘못된 테넌트로 요청이 보내졌는지
2) 자동 경고(Alerting)
지속적인 인가 실패는 보안 위협 또는 운영상의 문제를 의미할 수 있으므로
다음 기준으로 자동 알림을 설정할 수 있습니다.
- 동일 사용자에게 반복적으로 발생하는 인가 실패
- 특정 테넌트 영역에서 짧은 시간에 다수의 실패 발생
- 특정
permissionKey에 대한 전반적인 거부 증가 WITH_DESCENDANTS관련 스코프 오류 증가
9.2 테넌트/역할/권한 변경 감사 로그(Audit Log)
권한과 테넌트 구조는 보안적으로 매우 중요한 영역입니다.
따라서 다음 변경 작업은 모두 감사 로그로 남겨야 합니다.
1) 기록 대상
- Role 생성/수정/삭제
- Permission 세트 변경
- UserRole 부여 및 해제
- 테넌트 계층 변경(
parentTenantId변경, 이동, 병합 등) - 스키마 버전 변경 또는 문서 구조 변경
2) 로그에 포함할 정보
- 작업 수행자(
userId) - 변경 대상(
roleId,userId,tenantId등) - 변경 전 값 / 변경 후 값
- 변경 시각
- 변경 경로(API, 관리 UI 등)
이 로그는 권한 문제 발생 시 원인을 빠르게 추적하고
보안 사고에 대비한 필수 보호 장치 역할을 합니다.
9.3 캐시 모니터링
Effective Permissions 캐시가 안정적으로 유지되는지는
인가 성능과 직결됩니다.
1) 모니터링해야 할 지표
-
cache hit ratio
- 캐시 조회 성공률이 낮아지면 인가 서비스 부하가 증가합니다.
-
effectivePermissions재계산 빈도- 지나치게 자주 재계산된다면
UserRole 변경이 많거나 캐시 무효화 정책이 과도하기 때문일 수 있습니다.
- 지나치게 자주 재계산된다면
-
캐시 크기 및 메모리 사용량
- 사용자 수가 많아질수록 cached entry도 늘어날 수 있으므로 모니터링이 필요합니다.
2) 버전 기반 캐시 검증 모니터링
permissionVersionmismatch 발생 빈도- 캐시 재생성 시간
lineage변경으로 인한 스코프 재해석 여부
이 값들이 비정상적으로 증가하면
역할/권한 구조 또는 테넌트 구조 변경 작업이 많다는 신호입니다.
9.4 IoT 데이터 관제 모니터링
테넌트 기반 격리 모델은 IoT 기기 운영에서도 주기적으로 확인해야 합니다.
1) 잘못된 테넌트 데이터 유입 검출
- 동일 디바이스가 서로 다른
tenantId로 요청을 보내는지 감시 tenantIdmismatch 발생 시 경고 발생- 디바이스 등록 정보와 실제 수신되는
tenantId의 불일치 관찰
2) 데이터 누락 및 과도한 트래픽 모니터링
- 특정 테넌트에서 IoT 데이터가 급감하거나 과도하게 증가하는 경우
- 기기 연결 상태와 상태 보고 주기 등도 함께 모니터링
9.5 운영 상태 지표(Ops Metrics)
권한/조직 구조 문제는 비즈니스 운영 지연을 유발할 수 있으므로
다음 지표는 상시 모니터링을 권장합니다.
1) 인가 지연(latency)
- 평균 인가 판단 시간
- 95~99 percentile
- O(1) 구조가 유지되는지 주기적으로 확인
2) 조직 구조 변경 영향
- 테넌트 이동/추가 후
lineage재생성 실패 여부 - 스코프(scope) 적용 결과 점검
3) UI/업무 흐름 영향
- 특정 테넌트에서 메뉴나 기능이 의도치 않게 숨겨지거나 노출되는지
- 잘못된 권한 설정으로 인한 작업 불가 케이스 관찰
9.6 종합 운영 원칙
- 인가 실패는 단순 오류가 아니라 보안/정책/조직 구조의 이상 신호로 해석합니다.
- 사용자/역할/테넌트 변경은 모두 감사 로그로 추적합니다.
lineage기반 트리 구조와 스코프 적용이 정상적으로 동작하는지
정기적으로 검증합니다.- 캐시 hit ratio와 인가 요청 지연은 서비스 안정성의 핵심 지표입니다.
- IoT 기기의
tenantIdmismatch는 즉시 감지하고 처리해야 합니다.
3부. 설계 철학의 부연 설명과 향후 과제
이 문서는 앞서 정리한 1부/2부의 설계 구조가
어떤 사고 과정에서 도출되었는지,
왜 이 방향이 바람직한지,
그리고 아직 해결되지 않은 문제가 무엇인지 명확히 기록하기 위한 설명 파트입니다.
이미 구성한 모델은 충분히 동작할 수 있다는 직감을 갖고 있으나,
그 직감의 근거/한계/확장 조건을 문서로 남겨 두는 것이
향후 구조 변경 및 운영 전략 수립에 도움이 될 것입니다.
1. 테넌트를 디렉터리로 바라본 이유
전체 설계는 다음과 같은 비유를 기반으로 자연스럽게 확장되었습니다.
- Tenant = 디렉터리(Tree)
- Role / Permission = 디렉터리 ACL
- ABAC = 속성 기반 조건(조건부 ACL)
이 비유가 유효한 이유는 다음과 같습니다.
1.1 데이터 격리 모델을 단순하고 직관적으로 만든다
PC 디렉터리 구조의 원칙은 IoT SaaS의 요구와 정합성이 높습니다.
- 테넌트 간 데이터는 완전히 분리됩니다.
- 부모–자식 관계가 존재하지만, 접근 가능 여부는 별도의 정책으로 제어됩니다.
- 경로 구조(path)를 기반으로 조상/자손 판별이 명확해집니다.
이는 계층 기반 관제 + 데이터 격리라는 SaaS의 핵심 요구에 적합합니다.
1.2 접근 제어(ACL) 개념을 자연스럽게 재사용할 수 있다
- 디렉터리 ACL의 “특정 사용자/그룹은 R/W/X 가능”이라는 구조는
역할(Role)과 Permission 개념과 거의 동일한 추상화입니다. - 디렉터리 상속 여부는 RBAC의
scopeType(EXACT / WITH_DESCENDANTS)로 해석할 수 있습니다.
1.3 ABAC를 필요한 만큼만 얹을 수 있다
Windows ACL처럼 ABAC를 과도하게 허용하면 규칙이 폭발하고 운영 난도가 올라갑니다.
따라서 본 설계에서는 다음 원칙을 유지합니다.
- RBAC을 기본 구조로 사용
- Tenant 계층(scope)으로 1차 접근 범위를 제어
- ABAC는 필수 속성 중심으로 제한적으로 도입
2. 데이터 구조 선택에 대한 부연 설명
2.1 Tenant 구조를 디렉터리처럼 설계한 이유
tenantId, parentTenantId, lineage(path) 조합은 다음 목적을 충족합니다.
- 조상/자손 판별을 재귀 탐색 없이 prefix 비교만으로 처리
- 테넌트 이동/병합 시 lineage 재생성만으로 정책 전체 재해석 가능
- ABAC 적용 시 tenant.attribute 조회가 용이
prefix 기반 lineage는 O(1) 또는 O(depth) 수준으로
테넌트 구조 판별 비용을 최소화합니다.
2.2 역할(Role) 상속은 얕게 유지한다
역할 상속이 깊어질 경우:
- Permission 충돌 가능성 증가
- flatten 비용 증가
- 운영자가 역할 의미를 이해하기 어려움
실제 운영 환경에서는 TenantViewer → TenantOperator → TenantOwner 정도의
얕은 상속이 가장 현실적입니다.
2.3 UserRole(scope)은 전체 모델을 연결하는 핵심
UserRole에 scopeTenantId + scopeType을 둠으로써:
- 상위조직 관제
- 하위조직 제한
- 특정 테넌트 단위 권한 활성화
등을 단일 모델 내에서 자연스럽게 표현할 수 있습니다.
이는 디렉터리 ACL의 “부모 권한 상속 여부”를 데이터 모델로 정식화한 구조입니다.
3. 인가 처리 흐름과 성능 설계
3.1 요청마다 그래프 탐색을 하지 않는다
테넌트 계층 + 역할 상속 구조를 매 요청마다 계산하는 방식은 확장성이 떨어집니다.
따라서 다음 원칙을 따릅니다.
- 로그인 시
- 테넌트 전환 시
모든 계산을 완료하고, 요청 시에는:
effectivePermissions(userId, tenantId)조회- 권한 Set에서 permissionKey 존재 여부 확인
만 수행합니다.
이 방식은 대규모 IoT 환경에서도 인가 비용을 사실상 O(1)로 유지할 수 있게 합니다.
3.2 ABAC는 RBAC 위에 얇은 레이어로 유지
ABAC는 자유도가 높지만 다음 문제를 유발할 수 있습니다.
- 정책 폭발
- 디버깅 난이도 증가
- 운영 혼란
따라서 다음과 같은 3단계 구조를 유지합니다.
- RBAC로 1차 허용
- ABAC Policy로 조건 평가
- 최종 허용 여부 결정
4. 소규모 환경에서는 이미 성공적으로 동작하는 이유
현재 구조는 직관적으로 타당하며 소규모 환경에서는 성능이 충분합니다.
그 이유는 다음과 같습니다.
- 테넌트 수가 적어 prefix 비교 비용이 매우 작음
- 역할 상속 깊이가 얕아 flatten 비용이 작음
- UserRole 수가 적어 effectivePermissions 계산이 빠름
그러나 규모가 커질 경우 다음 병목이 예상됩니다.
- 테넌트 수 증가 → prefix 판별 최적화 필요
- 역할/권한 증가 → flatten 비용 상승 및 캐시 무효화 난도 증가
- ABAC 규칙 증가 → 조건 평가 비용 증가
- UserRole 증가 → effectivePermissions 캐시 확장 전략 필요
즉, 현재 구조는 옳지만 확장 시 고려할 영역이 명확합니다.
5. 향후 해결해야 할 과제 (Open Problems)
5.1 테넌트 상속 범위 정의
- 어떤 설정/문서를 상위가 하위에 상속할 것인가
- 상속된 문서를 하위가 override할 수 있는가
- cross-tenant read-only 정책을 어디까지 허용할 것인가
5.2 역할 상속 깊이 결정
- 현재 얕은 상속은 현실적이지만
기능 확장 시 더 깊은 상속이 필요할 가능성 존재 - 부모 권한 override 가능 여부도 미정
5.3 인가 캐시 무효화 전략
effectivePermissions 캐시 invalidation은 확장 시 가장 난도가 높은 부분입니다.
- UserRole 변경
- RolePermission 변경
- Tenant lineage 변경
이 모두 캐시 무효화를 유발하며,
대규모 환경에서는 무효화 비용이 병목이 될 수 있습니다.
5.4 ABAC 확장 여부
- region/tier 조건
- 조건부 읽기/쓰기
- 문서 타입별 ABAC 정책
ABAC는 점진적 확장이 필요합니다.
5.5 cross-tenant 문서 공유 정책
- 상위 테넌트가 공통 문서를 하위 테넌트에 배포할 것인가
- 참조 방향을 어떻게 제한할 것인가
- 하위조직에서 override 가능 여부
6. 결론
다음 개념적 틀은 IoT SaaS 데이터 격리/권한 모델에 매우 적합합니다.
- Tenant = 디렉터리
- Role/Permission = ACL
- scope = 디렉터리 상속 구조
- ABAC = 제한적 속성 조건
현재 설계는 소규모 환경에서는 충분히 동작하며
대규모 환경에서도 확장성을 확보할 수 있는 토대를 갖추고 있습니다.
다만 다음 요소들은 향후 개선이 필요합니다.
- 권한 상속 깊이
- 테넌트 이동/병합 시 인가 캐시 무효화 전략
- cross-tenant 문서 정책
- ABAC 확장 범위
- 역할 구성 충돌 처리
즉, 본 모델은 유효한 1차 형태이며
서비스 성장에 따라 지속적인 확장과 보완이 필요한 설계 기반입니다.
홈으로 가기