멀티 테넌트 Contact 페이지 관리 시스템 (ASP.NET Core Identity 기반)
추천 자료: ASP.NET Core 인증 및 권한 부여
이 문서에서는 ASP.NET Core Identity를 사용하여 멀티 테넌트 Contact 페이지를 관리하는 시스템을 구축하는 방법을 설명합니다. 이 시스템은 테넌트별로 개별적인 Contact 페이지를 보여주고, 관리자 전용 페이지에서 테넌트별 콘텐츠를 관리할 수 있습니다.
📋 기능 요구 사항
Contact 페이지의 테넌트별 구분
- 로그인한 사용자의 TenantName에 따라 맞춤형 Contact 페이지를 보여줍니다.
- 로그인하지 않은 사용자는 기본 테넌트(
Hawaso
)의 Contact 페이지를 볼 수 있습니다.
Contact 페이지의 관리자 관리 기능
/admin/contact-management
경로에서 관리자는 로그인한 사용자의 TenantName에 맞는 Contact 페이지의 내용을 입력 및 수정할 수 있습니다.
단일 레코드 구조
- Pages 테이블에 테넌트별로 단일 레코드를 유지하여 간단한 쿼리와 데이터 관리가 가능하도록 합니다.
📂 테이블 설계 (Pages 테이블)
Pages 테이블은 테넌트별 Contact 페이지의 콘텐츠를 관리합니다.
CREATE TABLE Pages (
Id INT IDENTITY(1,1) PRIMARY KEY, -- 기본 키 (Primary Key)
TenantName NVARCHAR(MAX) NOT NULL DEFAULT 'Hawaso', -- 테넌트명 (예: Hawaso, Tenant1 등)
PageName VARCHAR(50) NOT NULL DEFAULT 'Contact', -- 페이지명 (예: Contact, About 등)
Title NVARCHAR(200) NOT NULL, -- 페이지 제목
Content NVARCHAR(MAX) NOT NULL, -- 페이지의 본문 (HTML 또는 Plain Text)
LastUpdated DATETIME NOT NULL DEFAULT GETDATE() -- 마지막 수정 날짜 (기본값: 현재 시간)
);
컬럼명 | 데이터 유형 | 설명 |
---|---|---|
Id | INT (PK) | 기본 키 (Primary Key) |
TenantName | NVARCHAR(MAX) | 테넌트명 (예: Hawaso, Tenant1) |
PageName | VARCHAR(50) | 페이지명 (Contact, About 등) |
Title | NVARCHAR(200) | 페이지 제목 |
Content | NVARCHAR(MAX) | 페이지 본문 (HTML / Plain Text) |
LastUpdated | DATETIME | 마지막 수정 날짜 |
🛠️ 1. 모델 설계
Page.cs
public class Page
{
public int Id { get; set; }
public string TenantName { get; set; } = "Hawaso"; // 기본 테넌트명 Hawaso
public string PageName { get; set; } = "Contact"; // 기본 페이지명 Contact
public string Title { get; set; } = string.Empty; // 페이지 제목
public string Content { get; set; } = string.Empty; // 페이지 본문
public DateTime LastUpdated { get; set; } // 마지막 수정 날짜
}
🛠️ 2. Identity 사용자 모델 수정
ApplicationUser.cs에 TenantName 속성을 추가합니다.
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public string? TenantName { get; set; } = "Hawaso"; // 기본값 Hawaso
}
🛠️ 3. MyDbContext 설정
Pages 테이블을 추가합니다.
MyDbContext.cs
public class MyDbContext : IdentityDbContext<ApplicationUser>
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public DbSet<Page> Pages { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Page>().HasKey(p => p.Id);
builder.Entity<Page>().Property(p => p.TenantName).HasDefaultValue("Hawaso");
}
}
🛠️ 4. 마이그레이션 및 데이터베이스 업데이트
Pages 테이블과 AspNetUsers 테이블의 TenantName 속성을 추가합니다.
dotnet ef migrations add AddPagesAndTenantName
dotnet ef database update
🛠️ 5. Contact 페이지 (사용자 보기)
라우팅 경로: /contact
ContactComponent.razor
@page "/contact"
@using Hawaso.Data
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Identity
@inject UserManager<ApplicationUser> UserManager
@inject IHttpContextAccessor HttpContextAccessor
@inject ApplicationDbContext _dbContext
@inject AuthenticationStateProvider AuthenticationStateProvider
<PageTitle>Contact</PageTitle>
<h1>@Page?.Title</h1>
<div>
@((MarkupString)Page?.Content)
</div>
@code {
private Page? Page;
protected override async Task OnInitializedAsync()
{
// 로그인한 사용자의 TenantName을 가져오기
//[3] var user = await UserManager.GetUserAsync(HttpContextAccessor.HttpContext.User);
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = await UserManager.GetUserAsync(authState.User);
var tenantName = user?.TenantName ?? "Hawaso";
Page = await _dbContext.Pages
.Where(p => p.PageName == "Contact" && p.TenantName == tenantName)
.FirstOrDefaultAsync();
if (Page == null)
{
Page = await _dbContext.Pages
.Where(p => p.PageName == "Contact" && p.TenantName == "Hawaso")
.FirstOrDefaultAsync();
}
}
}
🛠️ 6. Contact 관리 페이지 (관리자용)
라우팅 경로: /admin/contact-management
ContactManagementComponent.razor
@page "/admin/contact-management"
@using Hawaso.Data
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Identity
@inject UserManager<ApplicationUser> UserManager
@inject IHttpContextAccessor HttpContextAccessor
@inject ApplicationDbContext _dbContext
@inject NavigationManager NavigationManager
@inject AuthenticationStateProvider AuthenticationStateProvider
<PageTitle>Contact Management</PageTitle>
<h1>Contact Management</h1>
<EditForm Model="Page" OnValidSubmit="SaveChanges">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label for="title">Title</label>
<InputText id="title" @bind-Value="Page.Title" class="form-control" />
</div>
<div class="mt-3">
<label for="content">Content</label>
@* <InputTextArea id="content" @bind-Value="Page.Content" class="form-control" rows="10" /> *@
<Hawaso.Components.CkEditorFourComponent @bind-Value="Page.Content"></Hawaso.Components.CkEditorFourComponent>
</div>
<button type="submit" class="btn btn-primary mt-3">Save Changes</button>
</EditForm>
@code {
private Page Page = new Page();
protected override async Task OnInitializedAsync()
{
//[2] var user = await UserManager.GetUserAsync(HttpContextAccessor.HttpContext.User);
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = await UserManager.GetUserAsync(authState.User);
var tenantName = user?.TenantName ?? "Hawaso";
Page = await _dbContext.Pages
.Where(p => p.PageName == "Contact" && p.TenantName == tenantName)
.FirstOrDefaultAsync();
if (Page == null)
{
Page = new Page
{
PageName = "Contact",
TenantName = tenantName
};
}
}
private async Task SaveChanges()
{
var existingContent = await _dbContext.Pages
.FirstOrDefaultAsync(p => p.PageName == "Contact" && p.TenantName == Page.TenantName);
if (existingContent != null)
{
existingContent.Title = Page.Title;
existingContent.Content = Page.Content;
existingContent.LastUpdated = DateTime.Now;
}
else
{
Page.LastUpdated = DateTime.Now;
_dbContext.Pages.Add(Page);
}
await _dbContext.SaveChangesAsync();
NavigationManager.NavigateTo("/contact");
}
}
📚 라우팅 요약
경로 | 설명 | 컴포넌트 이름 |
---|---|---|
/contact |
사용자 보기용 Contact 페이지 | ContactComponent.razor |
/admin/contact-management |
관리자용 Contact 관리 페이지 | ContactManagementComponent.razor |
🎉 정리
- 멀티 테넌트 Contact 페이지 시스템을 구축했습니다.
- 사용자 로그인 시 TenantName에 따라 맞춤형 콘텐츠가 표시됩니다.
- 관리자 관리 기능을 통해 Contact 페이지 내용을 편집하고 저장할 수 있습니다.
- 테넌트의 Contact 페이지 데이터는 Pages 테이블의 단일 레코드로 관리됩니다.
🔧 확장 가능성
- 캐싱 최적화: DB 쿼리를 줄이기 위해 In-Memory Cache 또는 Distributed Cache(Redis) 추가.
- 보안 강화:
/admin/contact-management
경로에 관리자 권한을 추가하여 접근을 제한. - 페이지 확장: 다른 페이지 (About, FAQ 등)도 Pages 테이블에 추가하여 관리할 수 있습니다.
이제 멀티 테넌트 Contact 페이지 관리 시스템이 완벽하게 준비되었습니다! 🚀
추천 자료: .NET Blazor에 대해 알아보시겠어요? .NET Blazor 알아보기를 확인해보세요!