> For the complete documentation index, see [llms.txt](https://docs.axuslab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.axuslab.com/1.-common-structures/multi_tenancy.md).

# 다중 테넌트 - Multi Tenancy

Multi Tenancy

#### 설명

일반적인 의미는 다중 테넌시는 부동산에서 의미를 가지고 있습니다. "다중 테넌시란 여러 개의 테넌트(임차인)가 하나의 부동산 재산을 공유하고 있는 임대 형태를 말합니다. 이는 주택이나 상업용 부동산에 모두 적용될 수 있으며, 다중 테넌시의 예로는 다음과 같은 경우가 있을 수 있습니다:

**아파트: 여러 가구가 하나의 아파트 단지에서 각각의 아파트를 임대하는 경우입니다.**

**사무 공간: 여러 기업이 하나의 사무 공간을 공유하는 공유 오피스 스페이스나 코워킹 스페이스도 다중 테넌시의 예입니다.**

**상업용 부동산: 여러 상업 테넌트가 하나의 상업용 건물 내에서 자리를 잡는 경우입니다.**

다중 테넌시는 임대료와 부동산 관리에 관련된 여러 문제를 다루어야 하므로, 임대 계약서와 관련 법규를 잘 이해하는 것이 중요합니다. 각 테넌트 간의 권리와 책임, 임대 조건, 관리 비용 등을 명확하게 정의하는 임대 계약서가 필요하며, 법적인 상담이나 부동산 전문가의 도움이 필요할 수 있습니다."

그렇지만 소프트웨어 에서는 아래와 같이 사용하고 있습니다.

"**다중 테넌트** 소프트웨어는 **단일 인스턴스**가 서버에서 실행되고 **여러 테넌트**에 서비스를 제공하는 소프트웨어 **아키텍처**를 의미합니다." 테넌트는 소프트웨어 인스턴스에 대한 특정 권한으로 공통 액세스를 공유하는 사용자 그룹입니다. 다중 테넌트 아키텍처를 사용하면 소프트웨어 프로그램은 모든 테넌트에게 데이터, 환경 구성, 사용자 관리, 테넌트 개별 기능 및 비 기능적 속성을 포함하여 인스턴스의 전용 범위를 제공하도록 디자인 되었습니다. ([Wikipedia](https://en.wikipedia.org/wiki/Multitenancy))

간단히 말해서 다중 테넌시는 **SaaS** (Software as-a Service) 애플리케이션을 만드는 데 사용되는 기술입니다.

![Alt text](/files/mJDXnXD2khL247FjRRK5)

#### 예제

**Enabling Multi-Tenancy**

다중 테넌시는 프레임 워크 수준에 대해 기본적으로 비활성화되어 있습니다. 아래와 같이 **AppConsts.cs** 파일에서 true로 세팅하면 다중 테넌시를 사용할 수 있습니다.

```csharp
// 다중 테넌트를 지원하는 애플리케이션 사용여부
public const bool MultiTenancyEnabled = true;
```

반대로 다중 테넌트를 사용하지 않을때는 false로 설정하고 프로젝트를 실행하면 된다.

**Host vs Tenant**

다중 테넌트 시스템에서 사용되는 두 가지 용어를 정의합니다 :

* **Tenant**: 자체 사용자, 역할, 권한, 설정이 있고 다른 테넌트와 완전히 격리 된 애플리케이션을 사용하는 고객. 다중 테넌트 애플리케이션에는 하나 이상의 테넌트가 있습니다. CRM 애플리케이션 인 경우 다른 테넌트도 자체 계정, 연락처, 제품 및 주문을 가지고 있습니다. 따라서 '**테넌트 사용자**'라고하면 테넌트가 소유 한 사용자를 의미합니다.
* **Host**: 호스트는 단일 호스트입니다. 호스트는 테넌트 생성 및 관리를 담당합니다. '**호스트 사용자**'는 더 높은 수준에 있으며 모든 테넌트와 독립적으로 제어 할 수 있습니다.

**Data Filters**

Axs는 엔티티에 대해 **IMustHaveTenant** 및 **IMayHaveTenant**의 두 인터페이스 중 하나를 구현할 때 필터가 자동으로 수행합니다.

**IMustHaveTenant Interface**

**TenantId** 속성을 정의하는 예제 :

```csharp
public class Product : Entity, IMustHaveTenant
{
    // null을 허용하지 않는 테넌트(오직 테넌트만 사용가능 하다)
    public int TenantId { get; set; }

    public string Name { get; set; }

    //...other properties
}
```

위와 같이 선언하고 사용하면 Axs는 테넌트 별 엔티티임을 인식하고 테넌트의 엔티티를 다른 테넌트와 자동으로 격리합니다.

**IMayHaveTenant interface**

호스트와 테넌트간에 **엔티티 유형**을 공유해야 할 수 있습니다. 따라서 엔티티는 테넌트 또는 호스트가 소유 할 수 있습니다. IMayHaveTenant 인터페이스는 또한 **TenantId** (IMustHaveTenant와 유사)를 정의하지만 이 경우 \*\*null을 허용(nullable)\*\*합니다. IMayHaveTenant를 구현하는 예제 :

```csharp
public class Role : Entity, IMayHaveTenant
{
    // null을 허용하는 테넌트(= 호스트)
    public int? TenantId { get; set; }

    public string RoleName { get; set; }

    //...other properties
}
```

**null** 값은 **호스트** 엔티티임을 의미하고 **null이 아닌** 값은 Id가 TenantId 인 **테넌트**의 값입니다.

**Host and Tenants 모드 변경**

다중 테넌트 애플리케이션 데이터베이스에서 작업하는 동안 **현재 테넌트**를 가져올 수 있습니다. 기본적으로 [IAxsSession](https://github.com/axuslab/docs.axs/blob/main/Developments/Session/README.md)에서 가져옵니다.

```csharp
public class SampleService : ITransient
{
    private readonly IRepository<Sample> _sampleRepository;
    private readonly IUnitOfWorkManager _unitOfWorkManager;

    public SampleService(IRepository<Sample> sampleRepository, IUnitOfWorkManager unitOfWorkManager)
    {
        _sampleRepository = sampleRepository;
        _unitOfWorkManager = unitOfWorkManager;
    }

    [UnitOfWork]
    public virtual List<Sample> GetSamples(int tenantId)
    {
        // 지정된 테넌트로 변경한다.
        using (_unitOfWorkManager.Current.SetTenantId(tenantId))
        {
            // using 안쪽코드에서는 변경된 테넌트로 실행이 된다.
            return _sampleRepository.GetAllList();
        }
    }
}
```

SetTenantId를 사용하지 않으면 세션([session](https://github.com/axuslab/docs.axs/blob/main/Developments/Session/README.md))에서 tenantId를 가져옵니다. 여기에는 몇 가지 지침과 모범 사례가 있습니다.

* **SetTenantId(null)** 를 사용하여 호스트로 전환합니다.
* **using** 블록 (예제에서와 같이) 내에서 SetTenantId를 사용합니다. 이렇게하면 using 블록이 끝날 때 tenantId가 자동으로 복원됩니다.

#### 확인

아래 코드를 통해 실행해보면 파라메터로 넘어온 테넌트의 정보에 따라서 리턴되는 데이터가 달라진다.

![picture 1](/files/90BL4cbodH2tal6pDfBC)

null로 넘기면 호스트의 정보를 가져오고, 숫자 1을 넘기면 해당 테넌트 범위의 값으로 데이터를 가져온다.

테넌트의 값을 null로 보냈을때의 데이터와 ![picture 3](/files/28zOCUcKYHJ9M7fYO0ST)

![picture 4](/files/MHFoFWr7McBNhtIN3u0Q)

테넌트의 값을 1로 보냈을때의 데이터를 확인하면 서로 다른것을 확인 할 수 있습니다.

![picture 6](/files/hC8Ue7nOPU1dbjno28b4)

![picture 5](/files/dTzVShoLfC1zRtmSu3bJ)
