Dev.Chan64's Blog

홈으로 가기
Show Cover Slide Show Cover Slide

1부. <데이터 격리, 테넌트 계층, 역할 상속, 인가> 모델

이 문서는 IoT 관제 SaaS에서 데이터 격리(multi-tenant separation),
사용자 권한 모델(RBAC/ABAC), 그리고 테넌트 및 역할 상속 구조
왜 이렇게 고민하게 되었는지를 설명하기 위한 설계 노트입니다.

완성된 설계를 기록하기 위한 문서가 아니라,
설계의 방향이 어떻게 도출되었는지와 현재 남아 있는 미해결 과제를
정리한 문서
입니다.


1. 출발점: 왜 이렇게 복잡한 구조를 생각하게 되었는가?

IoT 관제 SaaS는 여러 고객의 장비/사용자/데이터가
단일 플랫폼에서 동시에 운영되는 구조를 가집니다.
여기에는 다음과 같은 상반된 요구가 존재합니다.

  1. 고객 간 데이터는 완전히 분리되어야 합니다.
  2. 동일한 고객 내부에는 상위–하위 조직 구조가 존재합니다.

따라서 단일 테넌트 개념만으로는 실제 운영 요구를 충족할 수 없었습니다.

이로 인해 자연스럽게 다음 개념이 도출되었습니다.

결과적으로 PC 파일시스템의 디렉토리 구조 + Windows ACL 상속
모델
에서
많은 아이디어를 차용하게 되었습니다.


2. 데이터는 “문서(Document)” 중심으로 설계한다

IoT 장비/레시피/설정/진단/로그는 모두 트리 구조의 특성을 갖습니다.
그러나 하나의 문서가 지나치게 깊고 복잡한 트리를 갖게 되면:

이를 해결하기 위해 다음 원칙을 정했습니다.

문서(Document) 원칙

이 방식은 테넌트 간 데이터 혼합을 방지하면서
문서 단위의 유연한 확장을 가능하게 합니다.


3. 테넌트(Tenant)는 “트리(Tree)” 구조로 본다

테넌트는 PC의 디렉토리처럼 트리 구조로 관리하는 것이 자연스럽습니다.

예:

/root
/root/BrandA
/root/BrandA/Shop01
/root/BrandA/Shop02

이 구조는 다음 기능을 자연스럽게 제공합니다.

Tenant 구조 예시

tenantId
parentTenantId
path             // materialized path
type             // 조직 구분 (예: HQ, branch 등)
attributes       // ABAC용 속성

이 구조는 확장성이 높고, 운영/리포트/UI 모두에서 직관적입니다.


4. 역할(Role)은 “객체 상속”과 유사한 구조로 설계한다

실제 현장에서는 역할이 단순 태그 이상의 의미를 가집니다.

예:

이를 위해 Role에도 부모–자식 상속 구조를 적용했습니다.

Role
- roleId
- parentRoleId
- permissions[]

이를 통해 공통 권한은 부모 역할로 올리고,
추가 권한만 자식에 확장하여 구성할 수 있습니다.

상속 깊이는 운영 복잡도를 고려해 1~2단계가 현실적입니다.


5. UserRole에 “scope” 개념을 도입한다

Windows 디렉토리 권한처럼 역할의 유효 범위를 지정합니다.

UserRole
- userId
- roleId
- scopeTenantId   // 기준 테넌트
- scopeType       // EXACT | WITH_DESCENDANTS

예시:

이를 통해
계층형 테넌트 + 역할 상속 + 데이터 격리 요구를 모두 충족할 수
있습니다.


6. 인가 처리: 요청 시에는 그래프 탐색을 하지 않는다

테넌트 트리와 역할 상속 관계를
요청마다 탐색한다면 성능은 크게 저하됩니다.

핵심 원칙은 다음과 같습니다.

로그인 또는 테넌트 전환 시점에 모든 계산을 완료한다.

절차는 다음과 같습니다.

  1. 사용자가 특정 테넌트에서 갖는 모든 역할 조회
  2. 해당 역할들의 상속 구조를 펼쳐(flatten) 전체 권한 집합 구성
  3. 이 권한들을 Set으로 통합
  4. 캐시 또는 토큰에 저장

요청 시에는 단순히 다음만 검사합니다.

effectivePermissions.contains(permissionKey)

이 방식은 복잡한 RBAC 구조에서도
일관된 빠른 인가 성능을 제공합니다.


7. 왜 이런 설계를 선택했는가?

이 구조는 복잡한 아키텍처를 보여주기 위한 것이 아닙니다.
오히려 IoT 관제 SaaS의 도메인적 요구가 자연스럽게 요구한 형태입니다.

요구사항 ↔ 구조 대응표

요구사항 구조로 해결한 방식
고객 간 완전한 데이터 격리 tenantId 기반 문서 격리
본사–지점 구조 테넌트 계층(Tree)
역할 차등 Role 상속
상위 조직의 하위 관제 UserRole scope = WITH_DESCENDANTS
빠른 인가 판단 effectivePermissions 캐싱
문서 기반 확장성 스키마 기반 Document 모델

결국 이 설계는
<도메인 요구, 데이터 모델의 제약, 운영 현실>이 만나는 지점에서
도출된 구조입니다.


8. 아직 남아 있는 과제(미해결점)

8.1 테넌트 상속 범위 정의

8.2 역할 상속 깊이 제한

8.3 ABAC 적용 범위

8.4 effectivePermissions 무효화 전략

8.5 cross-tenant 참조 정책


9. 결론: 설계 노트의 의미

이 문서는 완성된 아키텍처 문서가 아닌,
왜 이런 구조를 고민하게 되었는지,
도메인의 본질적 문제를 어떻게 단순화하려 했는지,
아직 남아 있는 과제는 무엇인지를 기록한 메모이자 방향성 문서입니다.

IoT 관제 SaaS에서는 멀티테넌트 구조, 계층형 조직, 권한 모델,
문서 기반 데이터 모델이 필연적으로 요구됩니다.
현재 설계는 이러한 요구를 충족하는 초기 형태이며,
앞으로도 확장 가능성을 고려한 기반을 마련한 상태입니다.


2부. IoT SaaS 데이터 격리 및 권한 모델 설계

이 문서는 단순한 이론 설계가 아니라, 실제 IoT 관제 SaaS를 구축하고 운영하면서
테스트해 본 데이터 격리(multi-tenancy)권한/인가 모델을 정리한 기록입니다.

현재까지의 버전은 다음과 같은 특징을 가집니다.

이 구조는 현재까지 소규모 다중 테넌트 환경 + 제한된 수의 디바이스에서 운영/검증을 진행했습니다.

이 문서의 나머지 섹션(1–9)은 위에서 소개한 운영 버전을 기준으로

를 좀 더 정제된 설계 언어와 구조로 풀어 쓴 내용입니다.


1. 개요 (Overview)

이 문서는 IoT 기반 SaaS 플랫폼에서 데이터 격리(multi-tenancy)권한/인가(authorization) 구조를 어떻게 설계하고 구현할 수 있는지를 정리한 기술 문서입니다. 여기서 소개하는 접근 방식은 실제 운영 환경에서 테스트된 경험을 기반으로 하며, 테넌트 구조, 문서 기반 데이터 모델, 역할/권한 모델, IoT 디바이스 매핑 등 서로 다른 도메인을 하나의 일관된 구조로 통합하기 위해 고민한 결과입니다.

특히 다음 세 가지 목표를 중심으로 합니다.

  1. 고객/조직 단위의 완전한 데이터 분리
    각 테넌트는 독립된 데이터 영역을 가지며, 어떤 레벨에서도 다른 테넌트의 데이터가 섞이거나 노출되지 않도록 보장하는 구조를 제공합니다.

  2. 문서(Document) 기반의 유연한 데이터 모델 유지
    IoT 디바이스의 상태, 설정, 프로필, 레시피, 사용자 로그 등 다양한 데이터 타입을 모두 문서 단위로 통일해 관리할 수 있게 하며, 스키마(Schema)를 통해 구조적 안정성과 유효성을 보장합니다.

  3. 확장 가능한 권한/인가 모델 구축
    테넌트 수와 사용자 수가 증가해도 인가 판단이 부담 없이 작동하도록 구성하며, 다양한 역할(Role)과 권한(Permission)을 조합해 복잡한 SaaS 운영 시나리오를 표현할 수 있습니다.

이를 통해 전체 구조는 단순한 RBAC(Role-Based Access Control)를 넘어서
테넌트 계층 구조, 문서 중심 모델, IoT 디바이스 매핑까지 모두 포함하는
일종의 도메인 아키텍처(Architecture for Domain Isolation & Authorization)를 목표로 합니다.

이 문서의 이후 섹션들은 이러한 목표를 달성하기 위해 마련된
요구사항 → 도메인 모델 → 격리 전략 → 인가 모델 → 운영 시나리오 → 확장성 구조
순차적으로 설명합니다.


1.5 테넌트(Tenant)의 개념

이 문서에서 사용하는 테넌트(Tenant)라는 용어는
다른 SaaS 서비스에서 흔히 사용하는 단어를 차용한 것으로,
데이터를 조직 단위로 확실하게 분리하기 위해 정의한 논리적 데이터 구획 단위를 의미합니다.

기본적으로 테넌트는 다음과 같은 목적을 가집니다.

테넌트를 파일시스템 디렉터리로 바라본 이유

이 설계에서는 테넌트 구조를 파일시스템 디렉터리 구조와 유사한 개념으로 정의했습니다.
그 배경은 다음과 같습니다.

  1. 데이터 격리의 명확성

    • 디렉터리는 서로 다른 경로의 파일이 섞이지 않는 것처럼,
      각 테넌트는 다른 테넌트의 데이터와 절대 섞이지 않습니다.
  2. 확장성과 직관성

    • 조직이 상위/하위 구조를 가진다면,
      /root/organizationA/branch001 형태로 경로(path) 기반 트리로 표현하는 것이
      구조적 일관성과 직관성을 모두 충족합니다.
  3. 정책/권한 상속 모델을 단순하게 표현 가능

    • 디렉터리처럼 트리를 기반으로 하면
      “상위 조직이 하위 조직을 관제할 수 있다”는 정책을
      WITH_DESCENDANTS 같은 스코프(scope)로 간단히 표현할 수 있습니다.

테넌트가 포함하는 정보

테넌트는 다음 속성들을 가진 엔티티입니다.

테넌트가 이 문서에서 중요한 이유

이 문서의 대부분의 설계는 다음과 같은 원칙에서 출발합니다.

따라서 테넌트는
이 문서 전체의 설계에서 데이터 격리의 기본 단위이자 권한 모델의 핵심 축으로 사용됩니다.


2. 요구사항 정리

IoT 관제 SaaS에서 데이터 격리와 권한/인가 모델을 설계하기 위해 먼저 충족해야 할 요구사항을 기능적 측면과 비기능적 측면으로 나누어 정의합니다. 이 요구사항들은 이후 도메인 모델과 인가 구조의 설계 기준이 됩니다.

2.1 기능 요구사항

1) 테넌트(Tenant) 단위의 독립적 관리

2) 사용자–테넌트–역할 구조

3) 문서(Document) 접근 제어

4) 기기(Device) 단위 접근 제어

5) 상위/하위 조직 구조 지원


2.2 비기능 요구사항

1) 인가 판단 성능

2) 확장성(Scalability)

3) 모델 변경의 안정성

4) IoT 데이터의 정확한 테넌트 라우팅


3. 도메인 모델

IoT SaaS 데이터 격리 구조는 문서(Document), 스키마(Schema), 테넌트(Tenant), 사용자(User), 역할(Role), 권한(Permission) 등 여러 엔티티가 상호작용하면서 구성됩니다. 이 섹션에서는 전체 구조를 이루는 핵심 도메인 모델을 정의하고, 각 모델이 데이터 격리 및 인가 판단에 어떻게 기여하는지 설명합니다.


3.1 문서(Document) 모델

이 설계에서 대부분의 데이터는 문서(Document)라는 단위로 저장됩니다. 문서는 IoT 장비 상태, 레시피, 프로필, 사용자 로그, 설정 등 다양한 데이터를 하나의 통합된 구조로 수용하는 역할을 합니다.

문서의 공통 속성

모든 문서는 공통적으로 다음 속성을 가집니다.

설계 원칙

  1. 스키마가 존재해야만 문서를 생성할 수 있습니다.
    임의의 구조를 가진 문서는 허용하지 않습니다.

  2. 문서 간 참조는 (schemaName, refId)로만 이루어집니다.
    경로 기반, 트리 기반 참조 등은 허용하지 않으며,
    문서 간 의존성은 모두 스키마 단위의 명시적 참조로 관리됩니다.

  3. 트리 구조는 필요할 때 문서를 분할해 구성합니다.
    한 문서 안에 지나치게 깊은 트리를 넣지 않고,
    여러 문서로 구성된 느슨한 트리 구조를 사용합니다.

이 모델을 통해 데이터는 강하게 구조화되면서도 충분히 유연함을 유지할 수 있습니다.


3.2 스키마(Schema)와 DTO

스키마는 문서의 구조(Structure), 데이터 타입, 의미적 제약조건을 정의하는 계약(Contract)입니다.
모든 문서는 해당 스키마에서 정의한 규칙에 따라 생성/수정/검증됩니다.

스키마의 구성 요소

각 스키마는 다음 요소들로 구성됩니다.

스키마 변경과 버전 관리

스키마는 시간이 지남에 따라 변경될 수 있습니다.
스키마 변경 시 다음 원칙을 따릅니다.


3.3 테넌트(Tenant)와 계층 구조

테넌트는 조직 단위 데이터 격리의 기본 단위이며,
파일시스템의 디렉터리처럼 계층 트리 구조로 확장될 수 있습니다.

테넌트의 핵심 속성

데이터 격리 원칙

  1. tenantId가 다르면 데이터는 항상 분리됩니다.
    문서, 기기 정보, 설정, 로그 등 모든 데이터가 포함됩니다.

  2. 부모–자식 관계는 데이터 접근을 자동으로 허용하지 않습니다.
    접근 여부는 항상 역할(Role) + 스코프(scope) 규칙으로 제어합니다.

  3. cross-tenant 참조는 명시적으로만 허용됩니다.
    예: 상위 조직이 공통 프로필 문서를 read-only로 하위 조직에 제공하는 경우.

이 구조는 단순한 격리 모델을 유지하면서도
계층 구조에 기반한 관제 기능을 유연하게 추가할 수 있게 합니다.


3.4 사용자(User), 역할(Role), 권한(Permission)

권한/인가 모델의 중심에는 사용자(User), 역할(Role), 권한(Permission)의 관계가 있습니다.

사용자(User)

역할(Role)

역할은 “어떤 행동을 할 수 있는지”를 묶어놓은 단위입니다.

예시:

권한(Permission)

권한은 다음 요소로 정의됩니다.

예시:

역할–권한 관계

사용자–역할(UserRole) 할당

사용자에게 역할을 부여할 때는 다음 형태로 저장합니다.

이 구조를 통해 같은 사용자라도
조직별/범위별로 다른 권한을 가질 수 있습니다.


4. 데이터 격리 전략

멀티테넌시 기반의 IoT SaaS에서 가장 중요한 목표는 서로 다른 조직의 데이터가 절대로 섞이지 않도록 보장하는 것입니다. 이 섹션에서는 테넌트(Tenant) 단위 격리를 중심으로, 문서 간 참조 규칙과 기기/로그/상태 데이터가 테넌트별로 안전하게 저장되도록 하는 전략을 정의합니다.


4.1 테넌트 단위 격리

데이터 격리의 핵심 원칙은 다음과 같습니다.

1) 모든 데이터는 반드시 하나의 테넌트에 속한다

이 모든 데이터 엔티티는 생성 시 반드시 tenantId를 포함해야 합니다.

2) IoT 기기–테넌트 매핑은 단일 원칙을 따른다

각 디바이스는 단일 tenantId에 속합니다.
기기가 전송하는 모든 데이터는 프로토콜 단에서 다음 과정을 거칩니다.

  1. 수신된 패킷에 포함된 tenantId를 확인
  2. 유효한 tenantId인지 검증
  3. 해당 tenantId의 데이터 영역에만 저장

이 흐름을 통해 기기에서 잘못된 테넌트로 데이터가 유입되는 상황을 원천 차단합니다.

3) 저장소 선택: 논리적 격리 vs 물리적 격리

테넌트 기반 데이터 분리는 저장 방식에 따라 두 가지 시나리오로 확장할 수 있습니다.

이 문서에서 설명하는 모델은 논리적 격리를 기본으로 하되,
물리적 분리로 확장될 수 있도록 테넌트 ID 기반 구조를 일관되게 유지합니다.


4.2 문서 간 참조 규칙

문서(Document) 기반 데이터 모델을 사용할 때,
문서 A가 문서 B를 참조하는 상황은 자주 발생합니다.
이때 데이터 격리를 해치지 않도록 다음 규칙을 따릅니다.

1) 기본 규칙: 동일 테넌트 간 참조만 허용

2) 예외: 상위/하위 조직 구조에서의 제한적 참조

운영을 위해 상위 조직이 하위 조직에게
공통 문서(예: 공통 레시피, 공통 기기 설정)를 제공해야 하는 경우가 있습니다.

이 경우 다음 조건에서 명시적으로 허용합니다.

이를 통해 격리를 유지하면서도 운영 측면의 유연성을 확보할 수 있습니다.

3) 참조 방식은 항상 (schemaName, refId) 조합

경로(Path), 조합키, 내부 트리 구조 같은 표현은 사용하지 않습니다.


5. 권한 및 인가 모델

테넌트 단위 데이터 격리를 완성하기 위해서는
데이터 접근을 허용할지 여부를 판단하는 인가(Authorization) 구조가 반드시 필요합니다.
이 섹션에서는 사용자(User), 역할(Role), 권한(Permission), 스코프(scope) 개념을 기반으로
용량이 증가하더라도 안정적으로 동작하는 인가 모델을 정의합니다.


5.1 기본 원칙

인가 판단은 다음 세 가지 요소를 기준으로 이루어집니다.

  1. userId
    요청을 수행하는 사용자 식별자

  2. tenantId (contextTenantId)
    사용자가 지금 접근하려는 테넌트 컨텍스트
    예: 관제 화면에서 사용자가 선택한 하위 조직 테넌트

  3. permissionKey
    수행하려는 행동을 식별하는 권한 키
    예:

    • DOCUMENT:READ:SCHEMA=BREW_PROFILE
    • DEVICE:COMMAND:SCOPE=OWNED_BY_TENANT

테넌트 기반 접근 원칙

스코프(Scope)의 핵심 개념

역할(Role)을 부여할 때
사용자가 어떤 테넌트 범위에서 그 역할을 행사할 수 있는지 정의합니다.

이 구조를 통해 다음과 같은 시나리오를 간단히 지원할 수 있습니다.


5.2 Effective Permissions 계산

인가 요청을 O(1)에 가깝게 처리하기 위해
사용자의 역할(Role)과 권한(Permission)을 사전에 계산해 두는 방식을 사용합니다.

Effective Permissions란?

사용자가 특정 테넌트의 컨텍스트에서
실제로 행사할 수 있는 최종 권한(permission)들의 집합을 의미합니다.

예시:
사용자 U가 테넌트 T에 대해

두 역할과 그 부모 역할의 Permission을 합성해
U가 T에서 사용할 수 있는 실제 권한 집합이 됩니다.

계산 규칙

Effective Permissions는 다음 조건의 UserRole들을 합성해 계산합니다.

  1. scopeType = EXACT
    scopeTenantId === contextTenantId인 경우 포함

  2. scopeType = WITH_DESCENDANTS
    contextTenantIdscopeTenantId의 하위 조직인 경우 포함

  3. 각 UserRole이 가진 역할(Role)에 포함된 Permission들을 수집

  4. 역할이 부모 역할을 가진 경우
    부모 역할들의 Permission도 포함해 평탄화(flatten)합니다.

언제 계산하나?

실제 요청 처리 흐름

  1. 클라이언트/서버는 헤더 또는 컨텍스트에서
    userId, tenantId를 식별합니다.

  2. 인가 서비스는 캐시에 저장된
    effectivePermissions(userId, tenantId)를 조회합니다.

  3. 요청에 필요한 permissionKey
    해당 Permission 집합에 포함되어 있는지 확인합니다.

  4. 포함되어 있으면 허용(Allow),
    포함되어 있지 않으면 거절(Deny)합니다.

성능적 이점


6. 인가 서비스 설계

권한 및 데이터 격리 모델을 실제 시스템에서 안정적으로 운용하기 위해서는
이를 책임지는 인가(Authorization) 서비스가 필요합니다.
이 서비스는 사용자–테넌트–역할 관계를 관리하고,
요청 시 권한 판단을 빠르게 수행하는 핵심 컴포넌트입니다.


6.1 역할(Role)과 책임

인가 서비스는 다음 기능을 수행합니다.

1) 사용자–테넌트–역할(UserRole) 관계 조회

2) Role / Permission 정의 관리

3) 테넌트 계층 기반 스코프 검증

4) Effective Permissions 계산 및 캐싱

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) 캐시 저장 위치

효율적인 인가 판단을 위해 캐시는 다음 두 가지 방식 중 하나 또는 둘 다 사용합니다.


2) 캐시 키 구조

캐시 키는 사용자와 테넌트 컨텍스트 조합으로 구성합니다.

effective:{userId}:{tenantId}

예:

effective:user-123:tenant-A1

이 구조는 서로 다른 테넌트 컨텍스트에서
동일 사용자가 다른 권한 세트를 가질 수 있는 상황을 명확하게 분리해 줍니다.


3) 캐시 무효화(Invalidation)

권한/역할/테넌트 정보가 변경되면
기존 캐시 결과가 더 이상 유효하지 않으므로 반드시 무효화해야 합니다.

다음 변경 이벤트는 캐시 무효화가 필요합니다.

캐시 무효화 방식은 다음 중 하나를 선택합니다.


4) 버전(Version) 기반 캐시 검증

대규모 환경에서는 캐시 무효화를 전파하기 위해
버전 기반 접근 방식을 적용할 수 있습니다.

권한 버전(permissionVersion) 전략

이 방식의 장점은 다음과 같습니다.


5) TTL(Time-to-Live) 옵션

선택적으로 TTL을 둘 수 있습니다.

본 문서에서는 명시적 무효화 + 버전 검증 조합을 우선 전략으로 합니다.


7. IoT 관제 시나리오 예시

지금까지 정의한 테넌트 구조, 문서 모델, 권한/인가 모델이
IoT 디바이스 운영 환경에서 실제로 어떻게 동작하는지를
세 가지 대표적인 시나리오를 통해 설명합니다.


7.1 기기–테넌트 매핑

각 IoT 디바이스(deviceId)는 반드시 하나의 테넌트에 속합니다.
이는 데이터 격리의 핵심 전제이며, 다음 원칙에 따라 동작합니다.

1) 디바이스 등록 시 테넌트 지정

디바이스는 등록 과정에서 다음 정보를 가집니다.

디바이스가 네트워크에 연결되거나 데이터를 전송할 때
서버는 패킷에 포함된 tenantId를 검증하여
올바른 테넌트에 속한 장비인지 확인합니다.

2) 데이터 수신 시 테넌트 검증

디바이스에서 전송되는 다음 데이터들은 모두
수신 시점에 tenantId를 기준으로 검증/저장됩니다.

서버는 tenantId를 기준으로 저장 위치를 결정하며,
다른 테넌트의 데이터 영역에는 절대로 기록되지 않습니다.

3) 잘못된 테넌트 데이터 방지

프로토콜 레벨에서 tenantId를 명시적으로 포함시키면
기기 오동작이나 잘못된 라우팅으로 인해
타 테넌트 데이터가 오염되는 상황을 방어할 수 있습니다.


7.2 관제 화면에서의 흐름

사용자가 웹 앱/모바일 앱 등에서
어떤 테넌트(조직)를 선택했는지에 따라 관제 화면의 모든 정보가 결정됩니다.

전체 흐름

  1. 사용자 로그인

    • 사용자 userId를 기준으로
      접근 가능한 테넌트 목록을 조회합니다.
  2. 사용자 선택에 따라 특정 테넌트 컨텍스트 활성화

    • 예: 사용자 A는 tenant-X1, tenant-X2 두 곳에 접근 가능
    • 사용자가 관제 UI에서 tenant-X2를 선택하면
      contextTenantId = tenant-X2가 됩니다.
  3. Effective Permissions 계산/로드

    • 로그인 또는 테넌트 선택 시
      effectivePermissions(userId, contextTenantId)를 캐시에서 조회합니다.
    • 캐시가 없거나 무효화된 경우 계산하여 캐시에 저장합니다.
  4. 인가 기반 UI 렌더링

    • 읽기 권한이 없는 문서는 목록에서 제외
    • 쓰기 권한이 없는 설정 페이지는 비활성화
    • 특정 기기 제어 버튼은 권한이 있을 때만 노출
  5. 데이터 조회

    • 선택된 테넌트에 속한 장비 목록, 레시피, 프로필, 로그 등을 쿼리합니다.
    • 이때 서버는 userId가 아니라 contextTenantId를 기준으로 데이터를 반환합니다.
  6. 요청별 인가 처리

    • 사용자가 특정 문서를 열거나 수정할 때
      permissionKey를 확인하여 Allow/Deny를 즉시 판단합니다.

7.3 상위 조직의 하위 조직 관제

이 구조는 상위 조직이 하위 조직 전체를 한 번에 관제하는 시나리오를 지원합니다.

예: 상위 조직 사용자가 하위 조직 전체를 조회하는 흐름

  1. 상위 조직의 사용자 U는 다음 역할을 가집니다.

    • roleId = TenantOperator
    • scopeTenantId = 상위조직
    • scopeType = WITH_DESCENDANTS
  2. 사용자 U는 UI에서 특정 하위 조직 테넌트를 선택할 수 있습니다.

    • 선택한 시점에 contextTenantId가 해당 하위 조직으로 전환됩니다.
  3. 인가 서비스는 다음을 판단합니다.

    • scopeType = WITH_DESCENDANTS
    • 상위 조직이 선택된 하위 조직의 조상인지 여부
      lineage 또는 parentTenantId 트리 구조로 검증
  4. 조건이 충족되면
    해당 하위 조직에 속한 문서/기기/로그를 조회할 수 있습니다.

접근 방향 규칙

장점


8. 버전 관리 및 확장성

멀티테넌트 IoT SaaS에서 데이터 모델과 권한 구조는
시간이 지남에 따라 변경될 가능성이 높습니다.
스키마, 역할, 테넌트 구조가 발전하더라도
기존 데이터와 인가 모델을 안전하게 유지할 수 있어야 합니다.
이 섹션에서는 장기 운영을 위한 버전 관리 전략
서비스가 성장할 때 고려해야 하는 확장성 구조를 설명합니다.


8.1 스키마 버전 관리

문서(Document)의 스키마는 지속적으로 확장/변경될 수 있습니다.
기존 데이터와 호환성을 유지하면서 변화에 대응하기 위해 다음 원칙을 따릅니다.

1) 스키마는 schemaName + schemaVersion 조합으로 관리

2) 문서에는 생성 당시 스키마 버전을 기록

3) 스키마 변경 시 마이그레이션 정책 수립

이렇게 하면 스키마 변경이 잦더라도 시스템 일관성을 유지할 수 있습니다.


8.2 Role / Permission 버전 관리

역할(Role)과 권한(Permission)도 변경될 수 있습니다.
특정 기능이 삭제되거나 새로운 기능이 추가되면
Permission 정의와 역할 구성이 바뀔 수 있습니다.

권장 방식

  1. 기존 Role을 수정하기보다는 새로운 Role 정의를 추가
  2. 기존 Permission을 수정하기보다는 새 PermissionKey를 추가
  3. 사용자(UserRole)에게 할당된 Role을 점진적으로 재할당
  4. 기존 Role은 deprecated 처리 후 일정 기간 뒤 삭제

이 방식은 운영 중 “역할 충돌”이나 “권한 오류”를 방지합니다.


8.3 테넌트 계층 구조 확장

운영 중 조직 구조(테넌트 트리)가 다음과 같이 변화할 수 있습니다.

이러한 변동이 발생하더라도 다음 원칙을 유지합니다.

1) 테넌트의 lineage(path)를 항상 최신 상태로 유지

조직 이동 또는 병합이 발생할 때

를 일관성 있게 재생성합니다.

2) WITH_DESCENDANTS 스코프는 lineage 기반으로 자동 재해석

테넌트 계층 구조가 변경되면
스코프(scope)의 유효 범위도 lineage 변화에 맞게 자동 조정됩니다.

3) 캐시 무효화

테넌트 이동/병합은 캐시된 Effective Permissions 결과를 무효화해야 합니다.
(섹션 6.3에서 설명한 전략 활용)


8.4 확장성 고려 사항

멀티테넌트 SaaS가 성장하면서 테넌트 수/사용자 수/기기 수가 크게 증가하더라도
인가 판단과 데이터 접근이 안정적으로 동작해야 합니다.
이 구조는 다음과 같은 이유로 대규모 환경에서도 확장성을 확보할 수 있습니다.

1) 대규모 테넌트 수

테넌트 계층이 깊어지거나 수백/수천 개로 늘어나더라도
조직 관계 판별(상위/하위 조직 여부)은 lineage 기반 prefix 비교만 수행하면 됩니다.

즉, 테넌트 수가 증가해도 lineage 계산의 비용은 미미합니다.

2) 대규모 사용자 수

인가 요청당 필요한 연산은 다음과 같습니다.

3) 대규모 IoT 디바이스 수

4) 물리적 격리로 확장 가능


8.5 장기 운영을 위한 설계 원칙 요약


9. 운영 및 모니터링

멀티테넌트 IoT SaaS에서 데이터 격리와 인가 모델이 안정적으로 동작하려면
운영 과정에서 시스템이 올바르게 동작하고 있는지 지속적으로 관찰하고 검증해야 합니다.
이 섹션은 운영 중 모니터링해야 할 핵심 지표, 로깅 전략, 장애 대응 관점에서의 고려사항을 정리합니다.


9.1 인가 실패 로그 관리

인가 실패는 잘못된 접근, 설정 오류, 역할 오할당, 테넌트 경계 문제를 식별하는 데 중요한 지표입니다.

1) 인가 실패 시 기록해야 할 정보

이 정보를 기록하면 운영자가 다음을 정확하게 파악할 수 있습니다.

2) 자동 경고(Alerting)

지속적인 인가 실패는 보안 위협 또는 운영상의 문제를 의미할 수 있으므로
다음 기준으로 자동 알림을 설정할 수 있습니다.


9.2 테넌트/역할/권한 변경 감사 로그(Audit Log)

권한과 테넌트 구조는 보안적으로 매우 중요한 영역입니다.
따라서 다음 변경 작업은 모두 감사 로그로 남겨야 합니다.

1) 기록 대상

2) 로그에 포함할 정보

이 로그는 권한 문제 발생 시 원인을 빠르게 추적하고
보안 사고에 대비한 필수 보호 장치 역할을 합니다.


9.3 캐시 모니터링

Effective Permissions 캐시가 안정적으로 유지되는지는
인가 성능과 직결됩니다.

1) 모니터링해야 할 지표

2) 버전 기반 캐시 검증 모니터링

이 값들이 비정상적으로 증가하면
역할/권한 구조 또는 테넌트 구조 변경 작업이 많다는 신호입니다.


9.4 IoT 데이터 관제 모니터링

테넌트 기반 격리 모델은 IoT 기기 운영에서도 주기적으로 확인해야 합니다.

1) 잘못된 테넌트 데이터 유입 검출

2) 데이터 누락 및 과도한 트래픽 모니터링


9.5 운영 상태 지표(Ops Metrics)

권한/조직 구조 문제는 비즈니스 운영 지연을 유발할 수 있으므로
다음 지표는 상시 모니터링을 권장합니다.

1) 인가 지연(latency)

2) 조직 구조 변경 영향

3) UI/업무 흐름 영향


9.6 종합 운영 원칙


3부. 설계 철학의 부연 설명과 향후 과제

이 문서는 앞서 정리한 1부/2부의 설계 구조가
어떤 사고 과정에서 도출되었는지,
왜 이 방향이 바람직한지,
그리고 아직 해결되지 않은 문제가 무엇인지 명확히 기록하기 위한 설명 파트입니다.

이미 구성한 모델은 충분히 동작할 수 있다는 직감을 갖고 있으나,
그 직감의 근거/한계/확장 조건을 문서로 남겨 두는 것이
향후 구조 변경 및 운영 전략 수립에 도움이 될 것입니다.


1. 테넌트를 디렉터리로 바라본 이유

전체 설계는 다음과 같은 비유를 기반으로 자연스럽게 확장되었습니다.

이 비유가 유효한 이유는 다음과 같습니다.

1.1 데이터 격리 모델을 단순하고 직관적으로 만든다

PC 디렉터리 구조의 원칙은 IoT SaaS의 요구와 정합성이 높습니다.

이는 계층 기반 관제 + 데이터 격리라는 SaaS의 핵심 요구에 적합합니다.

1.2 접근 제어(ACL) 개념을 자연스럽게 재사용할 수 있다

1.3 ABAC를 필요한 만큼만 얹을 수 있다

Windows ACL처럼 ABAC를 과도하게 허용하면 규칙이 폭발하고 운영 난도가 올라갑니다.

따라서 본 설계에서는 다음 원칙을 유지합니다.


2. 데이터 구조 선택에 대한 부연 설명

2.1 Tenant 구조를 디렉터리처럼 설계한 이유

tenantId, parentTenantId, lineage(path) 조합은 다음 목적을 충족합니다.

prefix 기반 lineage는 O(1) 또는 O(depth) 수준으로
테넌트 구조 판별 비용을 최소화합니다.

2.2 역할(Role) 상속은 얕게 유지한다

역할 상속이 깊어질 경우:

실제 운영 환경에서는 TenantViewer → TenantOperator → TenantOwner 정도의
얕은 상속이 가장 현실적입니다.

2.3 UserRole(scope)은 전체 모델을 연결하는 핵심

UserRole에 scopeTenantId + scopeType을 둠으로써:

등을 단일 모델 내에서 자연스럽게 표현할 수 있습니다.

이는 디렉터리 ACL의 “부모 권한 상속 여부”를 데이터 모델로 정식화한 구조입니다.


3. 인가 처리 흐름과 성능 설계

3.1 요청마다 그래프 탐색을 하지 않는다

테넌트 계층 + 역할 상속 구조를 매 요청마다 계산하는 방식은 확장성이 떨어집니다.
따라서 다음 원칙을 따릅니다.

  1. 로그인 시
  2. 테넌트 전환 시

모든 계산을 완료하고, 요청 시에는:

만 수행합니다.

이 방식은 대규모 IoT 환경에서도 인가 비용을 사실상 O(1)로 유지할 수 있게 합니다.

3.2 ABAC는 RBAC 위에 얇은 레이어로 유지

ABAC는 자유도가 높지만 다음 문제를 유발할 수 있습니다.

따라서 다음과 같은 3단계 구조를 유지합니다.

  1. RBAC로 1차 허용
  2. ABAC Policy로 조건 평가
  3. 최종 허용 여부 결정

4. 소규모 환경에서는 이미 성공적으로 동작하는 이유

현재 구조는 직관적으로 타당하며 소규모 환경에서는 성능이 충분합니다.

그 이유는 다음과 같습니다.

그러나 규모가 커질 경우 다음 병목이 예상됩니다.

즉, 현재 구조는 옳지만 확장 시 고려할 영역이 명확합니다.


5. 향후 해결해야 할 과제 (Open Problems)

5.1 테넌트 상속 범위 정의

5.2 역할 상속 깊이 결정

5.3 인가 캐시 무효화 전략

effectivePermissions 캐시 invalidation은 확장 시 가장 난도가 높은 부분입니다.

이 모두 캐시 무효화를 유발하며,
대규모 환경에서는 무효화 비용이 병목이 될 수 있습니다.

5.4 ABAC 확장 여부

ABAC는 점진적 확장이 필요합니다.

5.5 cross-tenant 문서 공유 정책


6. 결론

다음 개념적 틀은 IoT SaaS 데이터 격리/권한 모델에 매우 적합합니다.

현재 설계는 소규모 환경에서는 충분히 동작하며
대규모 환경에서도 확장성을 확보할 수 있는 토대를 갖추고 있습니다.

다만 다음 요소들은 향후 개선이 필요합니다.

즉, 본 모델은 유효한 1차 형태이며
서비스 성장에 따라 지속적인 확장과 보완이 필요한 설계 기반입니다.


홈으로 가기
태그: 회고 설계철학