KIM-JINO5 4 hónapja
szülő
commit
62d30045c8
31 módosított fájl, 3359 hozzáadás és 19 törlés
  1. 8 0
      .claude/settings.local.json
  2. 1 1
      Admin/Pages/Document/Index.cshtml
  3. 2 4
      Admin/Pages/Document/Index.cshtml.cs
  4. 162 0
      Admin/Pages/Member/Grade/Edit.cshtml
  5. 126 0
      Admin/Pages/Member/Grade/Edit.cshtml.cs
  6. 104 0
      Admin/Pages/Member/Grade/Index.cshtml
  7. 70 0
      Admin/Pages/Member/Grade/Index.cshtml.cs
  8. 107 0
      Admin/Pages/Member/Grade/Write.cshtml
  9. 100 0
      Admin/Pages/Member/Grade/Write.cshtml.cs
  10. 8 1
      Admin/using.cs
  11. 2 0
      Application/Abstractions/Data/IAppDbContext.cs
  12. 17 0
      Application/Features/MemberGrade/Create/Command.cs
  13. 56 0
      Application/Features/MemberGrade/Create/Handler.cs
  14. 6 0
      Application/Features/MemberGrade/Delete/Command.cs
  15. 27 0
      Application/Features/MemberGrade/Delete/Handler.cs
  16. 36 0
      Application/Features/MemberGrade/Get/Handler.cs
  17. 6 0
      Application/Features/MemberGrade/Get/Query.cs
  18. 17 0
      Application/Features/MemberGrade/Get/Response.cs
  19. 53 0
      Application/Features/MemberGrade/GetAll/Handler.cs
  20. 6 0
      Application/Features/MemberGrade/GetAll/Query.cs
  21. 22 0
      Application/Features/MemberGrade/GetAll/Response.cs
  22. 19 0
      Application/Features/MemberGrade/Update/Command.cs
  23. 63 0
      Application/Features/MemberGrade/Update/Handler.cs
  24. 9 1
      Domain/Entities/Members/MemberGrade.cs
  25. 2281 0
      Infrastructure/Infrastructure/Persistence/Migrations/20260205091913_a3.Designer.cs
  26. 30 0
      Infrastructure/Infrastructure/Persistence/Migrations/20260205091913_a3.cs
  27. 2 0
      Infrastructure/Persistence/AppDbContext.cs
  28. 13 12
      Infrastructure/Persistence/Configurations/Members/MemberGradeConfiguration.cs
  29. 5 0
      Infrastructure/Persistence/Migrations/AppDbContextModelSnapshot.cs
  30. 0 0
      SharedKernel/Extensions/ModelStateDictionaryExtensions.cs
  31. 1 0
      SharedKernel/SharedKernel.csproj

+ 8 - 0
.claude/settings.local.json

@@ -0,0 +1,8 @@
+{
+  "permissions": {
+    "allow": [
+      "Bash(dotnet build:*)",
+      "Bash(dotnet ef migrations add:*)"
+    ]
+  }
+}

+ 1 - 1
Admin/Pages/Document/Index.cshtml

@@ -86,7 +86,7 @@
                             <td>
                                 <div class="d-xl-flex gap-2 justify-content-center d-grid">
                                     <a class="btn btn-sm btn-outline-info" href="@row.EditURL">수정</a>
-                                    <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID" data-subject="@row.Subject">삭제</button>
+                                    <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
                                 </div>
                             </td>
                         </tr>

+ 2 - 4
Admin/Pages/Document/Index.cshtml.cs

@@ -35,8 +35,7 @@ namespace Admin.Pages.Document
             string Views,
             string? UpdatedAt,
             string CreatedAt,
-            string EditURL,
-            string DeleteURL
+            string EditURL
         )> List { get; set; } = [];
 
         public Pagination? Pagination { get; set; }
@@ -62,8 +61,7 @@ namespace Admin.Pages.Document
                 c.Views,
                 c.UpdatedAt,
                 c.CreatedAt,
-                EditURL: $"/Document/Edit/{c.ID}{Request.QueryString}",
-                DeleteURL: $"/Document/Delete/{c.ID}{Request.QueryString}"
+                EditURL: $"/Document/Edit/{c.ID}{Request.QueryString}"
             ))];
 
 

+ 162 - 0
Admin/Pages/Member/Grade/Edit.cshtml

@@ -0,0 +1,162 @@
+@page "{id:int}"
+@model Admin.Pages.Member.Grade.EditModel
+@{
+    ViewData["Title"] = "회원등급 수정";
+}
+
+<div class="container">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form name="f_admin_write" id="fAdminWrite" method="post" accept-charset="utf-8" autocomplete="off" enctype="multipart/form-data">
+        <input type="hidden" asp-for="Input.ID" />
+
+        <div class="row mb-2">
+            <label class="col-sm-2 col-form-label"><span class="text-danger">*</span> PK</label>
+            <div class="col-sm-10">
+                <input type="text" readonly class="form-control-plaintext" value="@Model.Input.ID" />
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.ImageFile" class="col-sm-2 col-form-label">이미지</label>
+            <div class="col-sm-10">
+                @if (Model.Input.Image is not null && Model.Input.Image != string.Empty)
+                {
+                    <img src="@Url.Content(Model.Input.Image)" class="img-fluid img-thumbnail" alt="@Model.Input.KorName" />
+                    <div class="form-check pt-2">
+                        <input type="checkbox" name="isImageRemove" id="isImageRemove" class="form-check-input" value="true" />
+                        <label for="isImageRemove" class="form-check-label">삭제</label>
+                    </div>
+
+                    <input type="hidden" asp-for="Input.Image" class="form-control" />
+                }
+                else
+                {
+                    <div id="memberGradePrev" hidden>
+                        <img class="img-fluid img-thumbnail" alt="이미지 미리보기" /><br/>
+                        <button type="button" class="btn btn-sm btn-danger mt-2 mb-2 btn-remove-preview">삭제</button>
+                    </div>
+                    <input type="file" asp-for="Input.ImageFile" class="form-control" accept="image/*" />
+                    <span asp-validation-for="Input.ImageFile" class="text-danger"></span>
+                }
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.KorName" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 한글 명</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.KorName" class="form-control" required />
+                <span asp-validation-for="Input.KorName" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.EngName" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 영문 명</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.EngName" class="form-control" required />
+                <span asp-validation-for="Input.EngName" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.Description" class="col-sm-2 col-form-label">내용</label>
+            <div class="col-sm-10">
+                <textarea asp-for="Input.Description" class="form-control" placeholder="최대 1000자" rows="2" maxlength="1000"></textarea>
+                <span asp-validation-for="Input.Description" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.Order" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 순서</label>
+            <div class="col-sm-10">
+                <input type="number" asp-for="Input.Order" class="form-control d-inline w-auto" min="-9999" max="9999" required />
+                <span asp-validation-for="Input.Order" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TotalDonationCount" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 누적 후원 횟수</label>
+            <div class="col-sm-10">
+                <div class="input-group">
+                    <input type="number" asp-for="Input.TotalDonationCount" class="form-control w-auto flex-grow-0" min="0" max="1000000000" required />
+                    <span asp-validation-for="Input.TotalDonationCount" class="text-danger"></span>
+                    <div class="input-group-text">회</div>
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TotalDonationAmount" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 누적 후원 금액</label>
+            <div class="col-sm-10">
+                <div class="input-group">
+                    <input type="number" asp-for="Input.TotalDonationAmount" class="form-control w-auto flex-grow-0" min="0" max="1000000000" required />
+                    <span asp-validation-for="Input.TotalDonationAmount" class="text-danger"></span>
+                    <div class="input-group-text">원</div>
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TextColor" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 표시 색상</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.TextColor" class="form-control w-auto flex-grow-0" minlength="4" maxlength="7" required />
+                <div class="form-text text-muted">
+                    색상 코드는 #RRGGBB 형식으로 입력하세요. (예: #FF5733)
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.IsActive" class="col-sm-2 col-form-label">사용 여부</label>
+            <div class="col-sm-10 align-content-center">
+                <div class="form-check-inline">
+                    <input type="checkbox" asp-for="Input.IsActive" class="form-check-input" />
+                    <label class="form-check-label" asp-for="Input.IsActive">
+                        사용합니다.
+                    </label>
+                    <span asp-validation-for="Input.IsActive" class="text-danger"></span>
+                </div>
+            </div>
+        </div>
+        @if (Model.Input.UpdatedAt is not null)
+        {
+            <div class="row mb-2">
+                <label class="col-sm-2 col-form-label">수정일시</label>
+                <div class="col-sm-10">
+                    <input asp-for="Input.UpdatedAt" class="form-control-plaintext" type="text" readonly />
+                </div>
+            </div>
+        }
+        @if (Model.Input.CreatedAt != null)
+        {
+            <div class="row mb-2">
+                <label class="col-sm-2 col-form-label">등록일시</label>
+                <div class="col-sm-10">
+                    <input asp-for="Input.CreatedAt" class="form-control-plaintext" type="text" readonly />
+                </div>
+            </div>
+        }
+        <hr />
+        <div class="d-grid gap-2 text-center d-md-block">
+            <button type="submit" class="btn btn-sm btn-success">저장</button>
+            <a asp-page="Index" class="btn btn-sm btn-secondary">취소</a>
+        </div>
+        <br />
+    </form>
+</div>
+
+@section Scripts {
+    <script>
+        setupImagePreview("Input_ImageFile", "memberGradePrev");
+
+        // 이미지 삭제
+        let oldImageSrc = "";
+        document.getElementById("isImageRemove")?.addEventListener("change", function (e) {
+            let image = document.querySelector("img.img-thumbnail");
+            if (image) {
+                if (e.target.checked) {
+                    oldImageSrc = image.src;
+                    image.src = "";
+                    image.style.display = "none";
+                } else {
+                    image.src = oldImageSrc;
+                    image.style.display = "";
+                }
+            }
+        });
+    </script>
+}

+ 126 - 0
Admin/Pages/Member/Grade/Edit.cshtml.cs

@@ -0,0 +1,126 @@
+using SharedKernel.Attributes;
+using SharedKernel.Extensions;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace Admin.Pages.Member.Grade
+{
+    public class EditModel(IMediator mediator) : PageModel
+    {
+        [BindProperty]
+        public InputModel Input { get; set; } = new();
+
+        public sealed class InputModel
+        {
+            [DisplayName("ID")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            public int ID { get; set; }
+
+            public string? Image { get; set; }
+
+            [DisplayName("이미지")]
+            [AllowedExtensions("jpg,jpeg,png,gif,webp", ErrorMessage = "이미지 형식은 jpg, jpeg, png, gif, webp 파일이어야 합니다.")]
+            public IFormFile? ImageFile { get; set; }
+
+            [DisplayName("한글 명")]
+            [DataType(DataType.Text)]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(240, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string KorName { get; set; } = default!;
+
+            [DisplayName("영문 명")]
+            [DataType(DataType.Text)]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(120, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string EngName { get; set; } = default!;
+
+            [DisplayName("내용")]
+            [DataType(DataType.Text)]
+            [StringLength(1000, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string? Description { get; set; }
+
+            [DisplayName("순서")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(-9999, 9999, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public short Order { get; set; } = 0;
+
+            [DisplayName("누적 후원 횟수")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(0, 1000000000, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public long TotalDonationCount { get; set; } = 0;
+
+            [DisplayName("누적 후원 금액")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(0, 1000000000, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public int TotalDonationAmount { get; set; } = 0;
+
+            [DisplayName("표시 색상")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(7, MinimumLength = 4, ErrorMessage = "{0}은(는) {2}자 이상 {1}자 이하로 입력하세요.")]
+            public string TextColor { get; set; } = "#000000";
+
+            [DisplayName("사용 여부")]
+            public bool IsActive { get; set; } = false;
+
+            public string? UpdatedAt { get; set; }
+            public string CreatedAt { get; set; } = default!;
+        }
+
+        public async Task OnGetAsync(int id, CancellationToken ct)
+        {
+            var result = await mediator.Send(new GetMemberGrade.Query(id), ct);
+
+            Input = new InputModel
+            {
+                ID = result.ID,
+                Image = result.Image,
+                KorName = result.KorName,
+                EngName = result.EngName,
+                Description = result.Description,
+                Order = result.Order,
+                TotalDonationCount = result.RequiredAttendance,
+                TotalDonationAmount = result.RequiredExp,
+                TextColor = result.TextColor ?? "#000000",
+                IsActive = result.IsActive,
+                UpdatedAt = result.UpdatedAt.GetDateAt(),
+                CreatedAt = result.CreatedAt.GetDateAt()
+            };
+        }
+
+        public async Task<IActionResult> OnPostAsync(bool isImageRemove, CancellationToken ct)
+        {
+            try
+            {
+                if (!ModelState.IsValid)
+                {
+                    throw new Exception(ModelState.GetErrorMessages());
+                }
+
+                await mediator.Send(new UpdateMemberGrade.Command(
+                    Input.ID,
+                    Input.KorName,
+                    Input.EngName,
+                    Input.Description,
+                    Input.Order,
+                    Input.ImageFile,
+                    isImageRemove,
+                    Input.TextColor,
+                    Input.TotalDonationAmount,
+                    Input.TotalDonationCount,
+                    Input.IsActive
+                ), ct);
+
+                TempData["SuccessMessage"] = $"{Input.KorName} 회원등급이 수정되었습니다.";
+            }
+            catch (Exception e)
+            {
+                TempData["ErrorMessages"] = e.Message;
+            }
+
+            return Redirect($"/Member/Grade/Edit/{Input.ID}");
+        }
+    }
+}

+ 104 - 0
Admin/Pages/Member/Grade/Index.cshtml

@@ -0,0 +1,104 @@
+@page
+@model Admin.Pages.Member.Grade.IndexModel
+@{
+    ViewData["Title"] = "회원 등급";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <a class="btn btn-sm btn-success" href="/Member/Grade/Write">추가</a>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table class="table table-bordered mt-3">
+            <colgroup>
+                <col width="*"/>
+                <col width="140px"/>
+                <col width="20%"/>
+                <col width="*"/>
+                <col width="*"/>
+                <col width="*"/>
+                <col width="*"/>
+                <col width="10%"/>
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>No</th>
+                    <th rowspan="2">이미지</th>
+                    <th rowspan="2">등급명</th>
+                    <th>순서</th>
+                    <th>누적 후원 수</th>
+                    <th rowspan="2">사용 여부</th>
+                    <th>등록일시</th>
+                    <th rowspan="2">비고</th>
+                </tr>
+                <tr>
+                    <th>ID</th>
+                    <th>회원 수</th>
+                    <th>누적 후원금</th>
+                    <th>수정일시</th>
+                </tr>
+            </thead>
+            @if (Model.Data == null || Model.Data.Count <= 0)
+            {
+                <tr>
+                    <td colspan="8">No Data.</td>
+                </tr>
+            }
+            else
+            {
+                @foreach (var row in Model.Data)
+                {
+                <tbody class="striped">
+                    <tr>
+                        <td>@row.Num</td>
+                        <td rowspan="2">
+                            @if (!string.IsNullOrEmpty(row.Image))
+                            {
+                                <img src="@row.Image" class="img-thumbnail" alt="@row.Name" />
+                            } else {
+                                <text>-</text>
+                            }
+                        </td>
+                        <td rowspan="2">
+                            <span style="color: @row.TextColor;">@row.Name</span>
+                        </td>
+                        <td>@row.Order</td>
+                        <td>@row.TotalDonationCount</td>
+                        <td rowspan="2">@row.IsActive</td>
+                        <td>@row.CreatedAt</td>
+                        <td rowspan="2">
+                            <div class="d-grid gap-2 d-block d-xxl-inline">
+                                <a class="btn btn-sm btn-outline-info" href="@row.EditURL">수정</a>
+                                <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" hidden />
+                                <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                            </div>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td>@row.ID</td>
+                        <td>@row.MemberRows</td>
+                        <td>@row.TotalDonationAmount</td>
+                        <td>@(row.UpdatedAt ?? "-")</td>
+                    </tr>
+                </tbody>
+                }
+            }
+        </table>
+    </div>
+</div>
+
+<!-- 삭제를 위한 -->
+<form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+    @Html.AntiForgeryToken()
+</form>

+ 70 - 0
Admin/Pages/Member/Grade/Index.cshtml.cs

@@ -0,0 +1,70 @@
+using SharedKernel.Extensions;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Admin.Pages.Member.Grade
+{
+    public class IndexModel(IMediator mediator) : PageModel
+    {
+        public int Total { get; private set; } = 0;
+
+        public List<RowModel> Data { get; private set; } = [];
+
+        public sealed class RowModel
+        {
+            public int Num { get; set; }
+            public int ID { get; set; }
+            public required string Name { get; set; }
+            public string? Image { get; set; }
+            public string? TextColor { get; set; }
+            public short Order { get; set; }
+            public long TotalDonationCount { get; set; }
+            public int TotalDonationAmount { get; set; }
+            public int MemberRows { get; set; }
+            public char IsActive { get; set; }
+            public string? UpdatedAt { get; set; }
+            public required string CreatedAt { get; set; }
+            public required string EditURL { get; set; }
+        }
+
+        public async Task OnGetAsync(CancellationToken ct)
+        {
+            var result = await mediator.Send(new GetMemberGrades.Query(), ct);
+
+            Total = result.Total;
+            Data = [.. result.List.Select(c => new RowModel
+            {
+                Num = c.Num,
+                ID = c.ID,
+                Name = c.KorName,
+                Image = c.Image,
+                TextColor = c.TextColor,
+                Order = c.Order,
+                TotalDonationCount = c.RequiredAttendance,
+                TotalDonationAmount = c.RequiredExp,
+                MemberRows = c.MemberRows,
+                IsActive = c.IsActive ? 'Y' : 'N',
+                UpdatedAt = c.UpdatedAt.GetDateAt(),
+                CreatedAt = c.CreatedAt.GetDateAt(),
+                EditURL = $"/Member/Grade/Edit/{c.ID}"
+            })];
+        }
+
+        public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+        {
+            try
+            {
+                await mediator.Send(new DeleteMemberGrade.Command(ids), ct);
+
+                TempData["SuccessMessage"] = "회원등급이 삭제되었습니다.";
+            }
+            catch (Exception e)
+            {
+                TempData["ErrorMessages"] = e.Message;
+            }
+
+            return RedirectToPage("/Member/Grade/Index");
+        }
+    }
+}

+ 107 - 0
Admin/Pages/Member/Grade/Write.cshtml

@@ -0,0 +1,107 @@
+@page
+@model Admin.Pages.Member.Grade.WriteModel
+@{
+    ViewData["Title"] = "회원등급 등록";
+}
+
+<div class="container">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form name="f_admin_write" id="fAdminWrite" method="post" accept-charset="utf-8" autocomplete="off" enctype="multipart/form-data">
+        <div class="row mb-2">
+            <label asp-for="Input.ImageFile" class="col-sm-2 col-form-label">이미지</label>
+            <div class="col-sm-10">
+                <div id="memberGradePrev" hidden>
+                    <img class="img-fluid img-thumbnail" alt="이미지 미리보기" /><br/>
+                    <button type="button" id="btnRemovePrev" class="btn btn-sm btn-danger mt-2 mb-2">삭제</button>
+                </div>
+                <input type="file" asp-for="Input.ImageFile" class="form-control" accept="image/*" />
+                <span asp-validation-for="Input.ImageFile" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.KorName" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 한글 명</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.KorName" class="form-control" required />
+                <span asp-validation-for="Input.KorName" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.EngName" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 영문 명</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.EngName" class="form-control" required />
+                <span asp-validation-for="Input.EngName" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.Description" class="col-sm-2 col-form-label">내용</label>
+            <div class="col-sm-10">
+                <textarea asp-for="Input.Description" class="form-control" placeholder="최대 1000자" rows="2" maxlength="1000"></textarea>
+                <span asp-validation-for="Input.Description" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.Order" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 순서</label>
+            <div class="col-sm-10">
+                <input type="number" asp-for="Input.Order" class="form-control d-inline w-auto" min="-9999" max="9999" required />
+                <span asp-validation-for="Input.Order" class="text-danger"></span>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TotalDonationCount" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 누적 후원 횟수</label>
+            <div class="col-sm-10">
+                <div class="input-group">
+                    <input type="number" asp-for="Input.TotalDonationCount" class="form-control w-auto flex-grow-0" min="0" max="1000000000" required />
+                    <span asp-validation-for="Input.TotalDonationCount" class="text-danger"></span>
+                    <div class="input-group-text">회</div>
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TotalDonationAmount" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 누적 후원 금액</label>
+            <div class="col-sm-10">
+                <div class="input-group">
+                    <input type="number" asp-for="Input.TotalDonationAmount" class="form-control w-auto flex-grow-0" min="0" max="1000000000" required />
+                    <span asp-validation-for="Input.TotalDonationAmount" class="text-danger"></span>
+                    <div class="input-group-text">원</div>
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.TextColor" class="col-sm-2 col-form-label"><span class="text-danger">*</span> 표시 색상</label>
+            <div class="col-sm-10">
+                <input type="text" asp-for="Input.TextColor" class="form-control w-auto flex-grow-0" minlength="4" maxlength="7" required />
+                <div class="form-text text-muted">
+                    색상 코드는 #RRGGBB 형식으로 입력하세요. (예: #FF5733)
+                </div>
+            </div>
+        </div>
+        <div class="row mb-2">
+            <label asp-for="Input.IsActive" class="col-sm-2 col-form-label">사용 여부</label>
+            <div class="col-sm-10 align-content-center">
+                <div class="form-check-inline">
+                    <input type="checkbox" asp-for="Input.IsActive" class="form-check-input" />
+                    <label class="form-check-label" asp-for="Input.IsActive">
+                        사용합니다.
+                    </label>
+                    <span asp-validation-for="Input.IsActive" class="text-danger"></span>
+                </div>
+            </div>
+        </div>
+        <hr/>
+        <div class="d-grid gap-2 text-center d-md-block">
+            <button type="submit" class="btn btn-sm btn-success">저장</button>
+            <a asp-page="Index" class="btn btn-sm btn-secondary">취소</a>
+        </div>
+        <br/>
+    </form>
+</div>
+
+@section Scripts {
+    <script>
+        setupImagePreview("Input_ImageFile", "memberGradePrev");
+    </script>
+}

+ 100 - 0
Admin/Pages/Member/Grade/Write.cshtml.cs

@@ -0,0 +1,100 @@
+using SharedKernel.Attributes;
+using SharedKernel.Extensions;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace Admin.Pages.Member.Grade
+{
+    public class WriteModel(IMediator mediator) : PageModel
+    {
+        [BindProperty]
+        public InputModel Input { get; set; } = new();
+
+        public sealed class InputModel
+        {
+            [DisplayName("이미지")]
+            [AllowedExtensions("jpg,jpeg,png,gif,webp", ErrorMessage = "이미지 형식은 jpg, jpeg, png, gif, webp 파일이어야 합니다.")]
+            public IFormFile? ImageFile { get; set; }
+
+            [DisplayName("한글 명")]
+            [DataType(DataType.Text)]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(240, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string KorName { get; set; } = default!;
+
+            [DisplayName("영문 명")]
+            [DataType(DataType.Text)]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(120, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string EngName { get; set; } = default!;
+
+            [DisplayName("내용")]
+            [DataType(DataType.Text)]
+            [StringLength(1000, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+            public string? Description { get; set; }
+
+            [DisplayName("순서")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(-9999, 9999, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public short Order { get; set; } = 0;
+
+            [DisplayName("누적 후원 횟수")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(0, 1000000000, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public long TotalDonationCount { get; set; } = 0;
+
+            [DisplayName("누적 후원 금액")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [Range(0, 1000000000, ErrorMessage = "{0} 입력 범위는 {1} ~ {2} 입니다.")]
+            public int TotalDonationAmount { get; set; } = 0;
+
+            [DisplayName("표시 색상")]
+            [Required(ErrorMessage = "{0}은(는) 필수입니다.")]
+            [StringLength(7, MinimumLength = 4, ErrorMessage = "{0}은(는) {2}자 이상 {1}자 이하로 입력하세요.")]
+            public string TextColor { get; set; } = "#000000";
+
+            [DisplayName("사용 여부")]
+            public bool IsActive { get; set; } = false;
+        }
+
+        public void OnGet()
+        {
+        }
+
+        public async Task<IActionResult> OnPostAsync(CancellationToken ct)
+        {
+            try
+            {
+                if (!ModelState.IsValid)
+                {
+                    throw new Exception(ModelState.GetErrorMessages());
+                }
+
+                await mediator.Send(new CreateMemberGrade.Command(
+                    Input.KorName,
+                    Input.EngName,
+                    Input.Description,
+                    Input.Order,
+                    Input.ImageFile,
+                    Input.TextColor,
+                    Input.TotalDonationAmount,
+                    Input.TotalDonationCount,
+                    Input.IsActive
+                ), ct);
+
+                TempData["SuccessMessage"] = $"{Input.KorName} 회원등급이 등록되었습니다.";
+
+                return RedirectToPage("/Member/Grade/Index");
+            }
+            catch (Exception e)
+            {
+                TempData["ErrorMessages"] = e.Message;
+
+                return Redirect("/Member/Grade/Write");
+            }
+        }
+    }
+}

+ 8 - 1
Admin/using.cs

@@ -46,4 +46,11 @@ global using SearchBannerItems = Application.Features.Banner.Item.Search;
 global using GetBannerItem = Application.Features.Banner.Item.Get;
 global using CreateBannerItem = Application.Features.Banner.Item.Create;
 global using UpdateBannerItem = Application.Features.Banner.Item.Update;
-global using DeleteBannerItem = Application.Features.Banner.Item.Delete;
+global using DeleteBannerItem = Application.Features.Banner.Item.Delete;
+
+// 회원 등급
+global using GetMemberGrades = Application.Features.MemberGrade.GetAll;
+global using GetMemberGrade = Application.Features.MemberGrade.Get;
+global using CreateMemberGrade = Application.Features.MemberGrade.Create;
+global using UpdateMemberGrade = Application.Features.MemberGrade.Update;
+global using DeleteMemberGrade = Application.Features.MemberGrade.Delete;

+ 2 - 0
Application/Abstractions/Data/IAppDbContext.cs

@@ -1,5 +1,6 @@
 using Microsoft.EntityFrameworkCore;
 using Domain.Entities.Common;
+using Domain.Entities.Members;
 using Domain.Entities.Page;
 using Domain.Entities.Page.Faq;
 using Domain.Entities.Page.Banner;
@@ -15,6 +16,7 @@ namespace Application.Abstractions.Data
         DbSet<FaqItem> FaqItem { get; set;  }
         DbSet<BannerPosition> BannerPosition { get; set;  }
         DbSet<BannerItem> BannerItem { get; set;  }
+        DbSet<MemberGrade> MemberGrade { get; set;  }
 
         Task<int> SaveChangesAsync(CancellationToken ct = default);
     }

+ 17 - 0
Application/Features/MemberGrade/Create/Command.cs

@@ -0,0 +1,17 @@
+using MediatR;
+using Microsoft.AspNetCore.Http;
+
+namespace Application.Features.MemberGrade.Create
+{
+    public sealed record Command(
+        string KorName,
+        string EngName,
+        string? Description,
+        short Order,
+        IFormFile? ImageFile,
+        string? TextColor,
+        int RequiredExp,
+        long RequiredAttendance,
+        bool IsActive
+    ) : IRequest;
+}

+ 56 - 0
Application/Features/MemberGrade/Create/Handler.cs

@@ -0,0 +1,56 @@
+using Application.Abstractions.Data;
+using SharedKernel.Storage;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.MemberGrade.Create
+{
+    public sealed class Handler(IAppDbContext db, IFileStorage fileStorage) : IRequestHandler<Command>
+    {
+        public async Task Handle(Command request, CancellationToken ct)
+        {
+            if (await db.MemberGrade.AnyAsync(x => x.KorName == request.KorName, ct))
+            {
+                throw new InvalidOperationException($"'{request.KorName}'은(는) 이미 등록된 한글 명입니다.");
+            }
+
+            if (await db.MemberGrade.AnyAsync(x => x.EngName == request.EngName, ct))
+            {
+                throw new InvalidOperationException($"'{request.EngName}'은(는) 이미 등록된 영문 명입니다.");
+            }
+
+            var memberGrade = Domain.Entities.Members.MemberGrade.Create(
+                request.KorName,
+                request.EngName,
+                request.Order,
+                request.IsActive
+            );
+
+            await db.MemberGrade.AddAsync(memberGrade, ct);
+            await db.SaveChangesAsync(ct);
+
+            FileStoragePath uploadPath = new(UploadTarget.Upload, UploadFolder.MemberGrade, memberGrade.ID);
+            string[] allowedFileExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
+            string? imagePath = null;
+
+            if (request.ImageFile is not null)
+            {
+                imagePath = (await fileStorage.SaveFileAsync(request.ImageFile, uploadPath, allowedFileExtensions, ct))?.Url;
+            }
+
+            memberGrade.Update(
+                request.KorName,
+                request.EngName,
+                request.Description,
+                request.Order,
+                imagePath,
+                request.TextColor,
+                request.RequiredExp,
+                request.RequiredAttendance,
+                request.IsActive
+            );
+
+            await db.SaveChangesAsync(ct);
+        }
+    }
+}

+ 6 - 0
Application/Features/MemberGrade/Delete/Command.cs

@@ -0,0 +1,6 @@
+using MediatR;
+
+namespace Application.Features.MemberGrade.Delete
+{
+    public sealed record Command(int[] IDs) : IRequest;
+}

+ 27 - 0
Application/Features/MemberGrade/Delete/Handler.cs

@@ -0,0 +1,27 @@
+using Application.Abstractions.Data;
+using SharedKernel.Storage;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.MemberGrade.Delete
+{
+    public sealed class Handler(IAppDbContext db, IFileStorage fileStorage) : IRequestHandler<Command>
+    {
+        public async Task Handle(Command request, CancellationToken ct)
+        {
+            if (request.IDs is null || request.IDs.Length == 0)
+            {
+                return;
+            }
+
+            var images = await db.MemberGrade.Where(c => request.IDs.Contains(c.ID)).Select(c => c.Image).ToListAsync(ct);
+
+            foreach (var img in images)
+            {
+                fileStorage.DeleteByUrl(img);
+            }
+
+            await db.MemberGrade.Where(c => request.IDs.Contains(c.ID)).ExecuteDeleteAsync(ct);
+        }
+    }
+}

+ 36 - 0
Application/Features/MemberGrade/Get/Handler.cs

@@ -0,0 +1,36 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.MemberGrade.Get
+{
+    public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+    {
+        public async Task<Response> Handle(Query request, CancellationToken ct)
+        {
+            var item = await db.MemberGrade
+                .AsNoTracking()
+                .FirstOrDefaultAsync(x => x.ID == request.ID, ct);
+
+            if (item is null)
+            {
+                throw new KeyNotFoundException("회원등급을 찾을 수 없습니다.");
+            }
+
+            return new Response(
+                item.ID,
+                item.KorName,
+                item.EngName,
+                item.Description,
+                item.Order,
+                item.Image,
+                item.TextColor,
+                item.RequiredExp,
+                item.RequiredAttendance,
+                item.IsActive,
+                item.UpdatedAt,
+                item.CreatedAt
+            );
+        }
+    }
+}

+ 6 - 0
Application/Features/MemberGrade/Get/Query.cs

@@ -0,0 +1,6 @@
+using MediatR;
+
+namespace Application.Features.MemberGrade.Get
+{
+    public sealed record Query(int ID) : IRequest<Response>;
+}

+ 17 - 0
Application/Features/MemberGrade/Get/Response.cs

@@ -0,0 +1,17 @@
+namespace Application.Features.MemberGrade.Get
+{
+    public sealed record Response(
+        int ID,
+        string KorName,
+        string EngName,
+        string? Description,
+        short Order,
+        string? Image,
+        string? TextColor,
+        int RequiredExp,
+        long RequiredAttendance,
+        bool IsActive,
+        DateTime? UpdatedAt,
+        DateTime CreatedAt
+    );
+}

+ 53 - 0
Application/Features/MemberGrade/GetAll/Handler.cs

@@ -0,0 +1,53 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.MemberGrade.GetAll
+{
+    public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+    {
+        public async Task<Response> Handle(Query request, CancellationToken ct)
+        {
+            var items = await db.MemberGrade
+                .AsNoTracking()
+                .OrderBy(c => c.Order)
+                .ThenByDescending(c => c.ID)
+                .Select(c => new
+                {
+                    c.ID,
+                    c.KorName,
+                    c.EngName,
+                    c.Description,
+                    c.Order,
+                    c.Image,
+                    c.TextColor,
+                    c.RequiredExp,
+                    c.RequiredAttendance,
+                    c.IsActive,
+                    c.UpdatedAt,
+                    c.CreatedAt
+                })
+                .ToListAsync(ct);
+
+            return new Response(
+                items.Count,
+                [.. items.Select((c, i) => new Response.Row(
+                    i + 1,
+                    c.ID,
+                    c.KorName,
+                    c.EngName,
+                    c.Description,
+                    c.Order,
+                    c.Image,
+                    c.TextColor,
+                    c.RequiredExp,
+                    c.RequiredAttendance,
+                    c.IsActive,
+                    0,
+                    c.UpdatedAt,
+                    c.CreatedAt
+                ))]
+            );
+        }
+    }
+}

+ 6 - 0
Application/Features/MemberGrade/GetAll/Query.cs

@@ -0,0 +1,6 @@
+using MediatR;
+
+namespace Application.Features.MemberGrade.GetAll
+{
+    public sealed record Query : IRequest<Response>;
+}

+ 22 - 0
Application/Features/MemberGrade/GetAll/Response.cs

@@ -0,0 +1,22 @@
+namespace Application.Features.MemberGrade.GetAll
+{
+    public sealed record Response(int Total, IReadOnlyList<Response.Row> List)
+    {
+        public sealed record Row(
+            int Num,
+            int ID,
+            string KorName,
+            string EngName,
+            string? Description,
+            short Order,
+            string? Image,
+            string? TextColor,
+            int RequiredExp,
+            long RequiredAttendance,
+            bool IsActive,
+            int MemberRows,
+            DateTime? UpdatedAt,
+            DateTime CreatedAt
+        );
+    }
+}

+ 19 - 0
Application/Features/MemberGrade/Update/Command.cs

@@ -0,0 +1,19 @@
+using MediatR;
+using Microsoft.AspNetCore.Http;
+
+namespace Application.Features.MemberGrade.Update
+{
+    public sealed record Command(
+        int ID,
+        string KorName,
+        string EngName,
+        string? Description,
+        short Order,
+        IFormFile? ImageFile,
+        bool IsImageRemove,
+        string? TextColor,
+        int RequiredExp,
+        long RequiredAttendance,
+        bool IsActive
+    ) : IRequest;
+}

+ 63 - 0
Application/Features/MemberGrade/Update/Handler.cs

@@ -0,0 +1,63 @@
+using Application.Abstractions.Data;
+using SharedKernel.Storage;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.MemberGrade.Update
+{
+    public sealed class Handler(IAppDbContext db, IFileStorage fileStorage) : IRequestHandler<Command>
+    {
+        public async Task Handle(Command request, CancellationToken ct)
+        {
+            var memberGrade = await db.MemberGrade.FirstOrDefaultAsync(x => x.ID == request.ID, ct);
+            if (memberGrade is null)
+            {
+                throw new KeyNotFoundException("회원등급을 찾을 수 없습니다.");
+            }
+
+            if (memberGrade.KorName != request.KorName && await db.MemberGrade.AnyAsync(x => x.KorName == request.KorName, ct))
+            {
+                throw new InvalidOperationException($"'{request.KorName}'은(는) 이미 등록된 한글 명입니다.");
+            }
+
+            if (memberGrade.EngName != request.EngName && await db.MemberGrade.AnyAsync(x => x.EngName == request.EngName, ct))
+            {
+                throw new InvalidOperationException($"'{request.EngName}'은(는) 이미 등록된 영문 명입니다.");
+            }
+
+            FileStoragePath uploadPath = new(UploadTarget.Upload, UploadFolder.MemberGrade, memberGrade.ID);
+            string[] allowedFileExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
+            string? imagePath = memberGrade.Image;
+
+            if (request.IsImageRemove && !string.IsNullOrEmpty(memberGrade.Image))
+            {
+                fileStorage.DeleteByUrl(memberGrade.Image);
+                imagePath = null;
+            }
+
+            if (request.ImageFile is not null)
+            {
+                if (!string.IsNullOrEmpty(memberGrade.Image))
+                {
+                    fileStorage.DeleteByUrl(memberGrade.Image);
+                }
+
+                imagePath = (await fileStorage.SaveFileAsync(request.ImageFile, uploadPath, allowedFileExtensions, ct))?.Url;
+            }
+
+            memberGrade.Update(
+                request.KorName,
+                request.EngName,
+                request.Description,
+                request.Order,
+                imagePath,
+                request.TextColor,
+                request.RequiredExp,
+                request.RequiredAttendance,
+                request.IsActive
+            );
+
+            await db.SaveChangesAsync(ct);
+        }
+    }
+}

+ 9 - 1
Domain/Entities/Members/MemberGrade.cs

@@ -14,6 +14,8 @@ namespace Domain.Entities.Members
 
         public string? Image { get; private set; }
 
+        public string? TextColor { get; private set; }
+
         public int RequiredExp { get; private set; } = 0;
 
         public long RequiredAttendance { get; private set; } = 0;
@@ -64,7 +66,7 @@ namespace Domain.Entities.Members
             return new(korName, engName, order, isActive);
         }
 
-        public void Update(string korName, string engName, string? description, short order, string? image, int requiredExp, long requiredAttendance, bool isActive)
+        public void Update(string korName, string engName, string? description, short order, string? image, string? textColor, int requiredExp, long requiredAttendance, bool isActive)
         {
             if (string.IsNullOrWhiteSpace(korName))
             {
@@ -101,6 +103,11 @@ namespace Domain.Entities.Members
                 throw new ArgumentOutOfRangeException(nameof(image));
             }
 
+            if (textColor is not null && textColor.Length > 7)
+            {
+                throw new ArgumentOutOfRangeException(nameof(textColor));
+            }
+
             if (requiredExp < 0)
             {
                 throw new ArgumentOutOfRangeException(nameof(requiredExp));
@@ -116,6 +123,7 @@ namespace Domain.Entities.Members
             Description = description;
             Order = order;
             Image = image;
+            TextColor = textColor;
             RequiredExp = requiredExp;
             RequiredAttendance = requiredAttendance;
             IsActive = isActive;

+ 2281 - 0
Infrastructure/Infrastructure/Persistence/Migrations/20260205091913_a3.Designer.cs

@@ -0,0 +1,2281 @@
+// <auto-generated />
+using System;
+using Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Infrastructure.Infrastructure.Persistence.Migrations
+{
+    [DbContext(typeof(AppDbContext))]
+    [Migration("20260205091913_a3")]
+    partial class a3
+    {
+        /// <inheritdoc />
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("ProductVersion", "10.0.2")
+                .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+            modelBuilder.Entity("Domain.Entities.Common.Config", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime>("LastUpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 수정일시");
+
+                    b.Property<byte[]>("RowVersion")
+                        .IsConcurrencyToken()
+                        .IsRequired()
+                        .ValueGeneratedOnAddOrUpdate()
+                        .HasColumnType("rowversion")
+                        .HasComment("동시성 제어용");
+
+                    b.HasKey("ID");
+
+                    b.ToTable("Config", null, t =>
+                        {
+                            t.HasComment("운영 정보 설정 값");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.EmailVerification.EmailVerifyNumber", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasMaxLength(10)
+                        .HasColumnType("nvarchar(10)")
+                        .HasComment("Code");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasMaxLength(60)
+                        .HasColumnType("nvarchar(60)")
+                        .HasComment("이메일");
+
+                    b.Property<DateTime>("Expiration")
+                        .HasColumnType("datetime2")
+                        .HasComment("만료 일시");
+
+                    b.Property<bool>("IsVerified")
+                        .HasColumnType("bit")
+                        .HasComment("인증 여부");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int")
+                        .HasComment("인증 유형 (이메일 인증 / 비밀번호 재설정)");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Code");
+
+                    b.HasIndex("Email");
+
+                    b.HasIndex("Expiration");
+
+                    b.HasIndex("IsVerified");
+
+                    b.HasIndex("Type");
+
+                    b.HasIndex("Type", "Code");
+
+                    b.HasIndex("Type", "Code", "IsVerified");
+
+                    b.ToTable("EmailVerifyNumber", null, t =>
+                        {
+                            t.HasComment("이메일 인증 번호들");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.EmailVerification.EmailVerifyToken", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Additional")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("추가 정보(JSON)");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasMaxLength(60)
+                        .HasColumnType("nvarchar(60)")
+                        .HasComment("이메일");
+
+                    b.Property<DateTime>("Expiration")
+                        .HasColumnType("datetime2")
+                        .HasComment("만료 일시");
+
+                    b.Property<bool>("IsVerified")
+                        .HasColumnType("bit")
+                        .HasComment("인증 여부");
+
+                    b.Property<string>("Token")
+                        .IsRequired()
+                        .HasMaxLength(256)
+                        .HasColumnType("nvarchar(256)")
+                        .HasComment("Token");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int")
+                        .HasComment("인증 유형 (이메일 인증 / 비밀번호 재설정)");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Email");
+
+                    b.HasIndex("Expiration");
+
+                    b.HasIndex("IsVerified");
+
+                    b.HasIndex("Token");
+
+                    b.HasIndex("Type");
+
+                    b.HasIndex("Type", "Email", "Token");
+
+                    b.HasIndex("Type", "Email", "Token", "IsVerified");
+
+                    b.ToTable("EmailVerifyToken", null, t =>
+                        {
+                            t.HasComment("이메일 인증 토큰들");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Channel", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("Handle")
+                        .HasMaxLength(30)
+                        .HasColumnType("nvarchar(30)")
+                        .HasComment("핸들");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("활성 여부");
+
+                    b.Property<bool>("IsVerified")
+                        .HasColumnType("bit")
+                        .HasComment("인증 여부");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(200)
+                        .HasColumnType("nvarchar(200)")
+                        .HasComment("채널 이름");
+
+                    b.Property<decimal>("PlatformFeeRate")
+                        .HasPrecision(5, 2)
+                        .HasColumnType("decimal(5,2)")
+                        .HasComment("수수료(%)");
+
+                    b.Property<string>("SID")
+                        .IsRequired()
+                        .HasMaxLength(24)
+                        .HasColumnType("nvarchar(24)")
+                        .HasComment("채널 ID");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.Property<string>("YouTubeUrl")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("YouTube 채널 URL");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Handle")
+                        .IsUnique()
+                        .HasFilter("[Handle] IS NOT NULL");
+
+                    b.HasIndex("MemberID")
+                        .IsUnique();
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.HasIndex("SID")
+                        .IsUnique();
+
+                    b.HasIndex("YouTubeUrl")
+                        .IsUnique();
+
+                    b.HasIndex("MemberID", "IsActive");
+
+                    b.HasIndex("MemberID", "IsVerified");
+
+                    b.HasIndex("MemberID", "IsVerified", "IsActive");
+
+                    b.ToTable("Channel", null, t =>
+                        {
+                            t.HasComment("채널 정보");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberEmailChangeLog", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("AfterEmail")
+                        .IsRequired()
+                        .HasMaxLength(60)
+                        .HasColumnType("nvarchar(60)")
+                        .HasComment("바뀐 이메일");
+
+                    b.Property<string>("BeforeEmail")
+                        .HasMaxLength(60)
+                        .HasColumnType("nvarchar(60)")
+                        .HasComment("이전 이메일");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(45)
+                        .HasColumnType("nvarchar(45)")
+                        .HasComment("IP Address");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Referer")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("이전 페이지 주소");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(512)
+                        .HasColumnType("nvarchar(512)")
+                        .HasComment("User Agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("CreatedAt");
+
+                    b.HasIndex("MemberID");
+
+                    b.ToTable("MemberEmailChangeLog", null, t =>
+                        {
+                            t.HasComment("사용자 이메일 변경 내역");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberIntroChangeLog", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("AfterIntro")
+                        .HasMaxLength(3000)
+                        .HasColumnType("nvarchar(3000)")
+                        .HasComment("바꾼 자기소개");
+
+                    b.Property<string>("BeforeIntro")
+                        .HasMaxLength(3000)
+                        .HasColumnType("nvarchar(3000)")
+                        .HasComment("이전 자기소개");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("IP Address");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Referer")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("이전 페이지 주소");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(512)
+                        .HasColumnType("nvarchar(512)")
+                        .HasComment("User Agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("MemberID");
+
+                    b.ToTable("MemberIntroChangeLog", null, t =>
+                        {
+                            t.HasComment("자기소개 변경 내역");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberLoginLog", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Account")
+                        .IsRequired()
+                        .HasMaxLength(120)
+                        .HasColumnType("nvarchar(120)")
+                        .HasComment("로그인 시도한 계정");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(45)
+                        .HasColumnType("nvarchar(45)")
+                        .HasComment("IP Address");
+
+                    b.Property<int?>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Reason")
+                        .HasMaxLength(225)
+                        .HasColumnType("nvarchar(225)")
+                        .HasComment("실패 이유");
+
+                    b.Property<string>("Referer")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("이전 페이지 주소");
+
+                    b.Property<bool>("Success")
+                        .HasColumnType("bit")
+                        .HasComment("로그인 성공 여부 (0: 실패, 1: 성공)");
+
+                    b.Property<string>("Url")
+                        .HasMaxLength(500)
+                        .HasColumnType("nvarchar(500)")
+                        .HasComment("요청 주소");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(512)
+                        .HasColumnType("nvarchar(512)")
+                        .HasComment("User Agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Account");
+
+                    b.HasIndex("MemberID");
+
+                    b.HasIndex("MemberID", "Success");
+
+                    b.ToTable("MemberLoginLog", null, t =>
+                        {
+                            t.HasComment("로그인 기록");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberNameChangeLog", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("AfterName")
+                        .HasMaxLength(40)
+                        .HasColumnType("nvarchar(40)")
+                        .HasComment("바꾼 별명");
+
+                    b.Property<string>("BeforeName")
+                        .HasMaxLength(40)
+                        .HasColumnType("nvarchar(40)")
+                        .HasComment("이전 별명");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("IP Address");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Referer")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("이전 페이지 주소");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(512)
+                        .HasColumnType("nvarchar(512)")
+                        .HasComment("User Agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("MemberID");
+
+                    b.ToTable("MemberNameChangeLog", null, t =>
+                        {
+                            t.HasComment("별명 변경 내역");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberSummaryChangeLog", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("AfterSummary")
+                        .HasMaxLength(50)
+                        .HasColumnType("nvarchar(50)")
+                        .HasComment("바꾼 한마디");
+
+                    b.Property<string>("BeforeSummary")
+                        .HasMaxLength(50)
+                        .HasColumnType("nvarchar(50)")
+                        .HasComment("이전 한마디");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("IP Address");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<string>("Referer")
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("이전 페이지 주소");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(512)
+                        .HasColumnType("nvarchar(512)")
+                        .HasComment("User Agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("MemberID");
+
+                    b.ToTable("MemberSummaryChangeLog", null, t =>
+                        {
+                            t.HasComment("한마디 변경 내역");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Member", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime?>("AuthCertifiedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("본인인증 일시");
+
+                    b.Property<DateOnly?>("Birthday")
+                        .HasColumnType("date")
+                        .HasComment("생년월일");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("가입 일시");
+
+                    b.Property<DateTime?>("DeletedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("탈퇴 일시");
+
+                    b.Property<DateTime?>("DeniedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("차단 일시");
+
+                    b.Property<string>("DeviceInfo")
+                        .HasMaxLength(400)
+                        .HasColumnType("nvarchar(400)")
+                        .HasComment("로그인 단말기 정보");
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasMaxLength(60)
+                        .HasColumnType("nvarchar(60)")
+                        .HasComment("이메일");
+
+                    b.Property<DateTime?>("EmailVerifiedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("이메일 인증 일시");
+
+                    b.Property<string>("FirstName")
+                        .HasMaxLength(20)
+                        .HasColumnType("nvarchar(20)")
+                        .HasComment("본명(성)");
+
+                    b.Property<string>("FullName")
+                        .HasMaxLength(40)
+                        .HasColumnType("nvarchar(40)")
+                        .HasComment("본명");
+
+                    b.Property<int?>("Gender")
+                        .HasColumnType("int")
+                        .HasComment("성별");
+
+                    b.Property<string>("Icon")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("아이콘");
+
+                    b.Property<string>("Intro")
+                        .HasMaxLength(1000)
+                        .HasColumnType("nvarchar(1000)")
+                        .HasComment("자기소개");
+
+                    b.Property<string>("IpAddress")
+                        .HasMaxLength(45)
+                        .HasColumnType("nvarchar(45)")
+                        .HasComment("IP Address");
+
+                    b.Property<bool>("IsAdmin")
+                        .HasColumnType("bit")
+                        .HasComment("운영진 여부");
+
+                    b.Property<bool>("IsAuthCertified")
+                        .HasColumnType("bit")
+                        .HasComment("본인 인증 여부");
+
+                    b.Property<bool>("IsCreator")
+                        .HasColumnType("bit")
+                        .HasComment("크리에이터 여부");
+
+                    b.Property<bool>("IsDenied")
+                        .HasColumnType("bit")
+                        .HasComment("차단 여부");
+
+                    b.Property<bool>("IsEmailVerified")
+                        .HasColumnType("bit")
+                        .HasComment("이메일 인증 여부");
+
+                    b.Property<bool>("IsWithdraw")
+                        .HasColumnType("bit")
+                        .HasComment("탈퇴 여부");
+
+                    b.Property<DateTime?>("LastEmailChangedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 이메일 변경 일시");
+
+                    b.Property<DateTime?>("LastIntroChangedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 자기소개 변경 일시");
+
+                    b.Property<DateTime?>("LastLoginAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 로그인 일시");
+
+                    b.Property<string>("LastLoginIp")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("마지막 로그인 IP");
+
+                    b.Property<string>("LastName")
+                        .HasMaxLength(40)
+                        .HasColumnType("nvarchar(40)")
+                        .HasComment("본명(이름)");
+
+                    b.Property<DateTime?>("LastNameChangedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 별명 변경 일시");
+
+                    b.Property<DateTime?>("LastSummaryChangedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("마지막 한마디 변경 일시");
+
+                    b.Property<int?>("MemberGradeID")
+                        .HasColumnType("int")
+                        .HasComment("회원등급 PK");
+
+                    b.Property<string>("Name")
+                        .HasMaxLength(20)
+                        .HasColumnType("nvarchar(20)")
+                        .HasComment("별명");
+
+                    b.Property<string>("Password")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("비밀번호");
+
+                    b.Property<DateTime>("PasswordUpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("비밀번호 변경 일시");
+
+                    b.Property<string>("Phone")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("연락처");
+
+                    b.Property<string>("SID")
+                        .IsRequired()
+                        .HasMaxLength(20)
+                        .HasColumnType("nvarchar(20)")
+                        .HasComment("SID");
+
+                    b.Property<string>("SignupIP")
+                        .HasMaxLength(15)
+                        .HasColumnType("nvarchar(15)")
+                        .HasComment("회원가입 시 IP");
+
+                    b.Property<string>("Summary")
+                        .HasMaxLength(50)
+                        .HasColumnType("nvarchar(50)")
+                        .HasComment("한마디");
+
+                    b.Property<string>("Thunmbnail")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("썸네일");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.Property<string>("UserAgent")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("User-agent");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("CreatedAt");
+
+                    b.HasIndex("DeletedAt");
+
+                    b.HasIndex("Email")
+                        .IsUnique();
+
+                    b.HasIndex("FullName");
+
+                    b.HasIndex("Gender");
+
+                    b.HasIndex("IsAdmin");
+
+                    b.HasIndex("IsAuthCertified");
+
+                    b.HasIndex("IsCreator");
+
+                    b.HasIndex("IsDenied");
+
+                    b.HasIndex("IsEmailVerified");
+
+                    b.HasIndex("IsWithdraw");
+
+                    b.HasIndex("MemberGradeID");
+
+                    b.HasIndex("Name")
+                        .IsUnique()
+                        .HasFilter("[Name] IS NOT NULL");
+
+                    b.HasIndex("Phone");
+
+                    b.HasIndex("SID")
+                        .IsUnique();
+
+                    b.ToTable("Member", null, t =>
+                        {
+                            t.HasComment("회원 정보");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.MemberApprove", b =>
+                {
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<DateTime?>("DisclosureInvestConsentAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("투자 현황 공개 동의 일시");
+
+                    b.Property<bool>("IsDisclosureInvest")
+                        .HasColumnType("bit")
+                        .HasComment("투자 현황 공개 여부");
+
+                    b.Property<bool>("IsReceiveEmail")
+                        .HasColumnType("bit")
+                        .HasComment("E-MAIL 수신 여부");
+
+                    b.Property<bool>("IsReceiveNote")
+                        .HasColumnType("bit")
+                        .HasComment("쪽지 수신 여부");
+
+                    b.Property<bool>("IsReceiveSMS")
+                        .HasColumnType("bit")
+                        .HasComment("SMS 수신 여부");
+
+                    b.Property<DateTime?>("ReceiveEmailConsentAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("E-MAIL 수신 동의 일시");
+
+                    b.Property<DateTime?>("ReceiveNoteConsentAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("쪽지 수신 동의 일시");
+
+                    b.Property<DateTime?>("ReceiveSMSConsentAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("SMS 수신 동의 일시");
+
+                    b.HasKey("MemberID");
+
+                    b.ToTable("MemberApprove", null, t =>
+                        {
+                            t.HasComment("회원 동의 및 수신 여부");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.MemberGrade", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("Description")
+                        .HasMaxLength(1000)
+                        .HasColumnType("nvarchar(1000)")
+                        .HasComment("설명");
+
+                    b.Property<string>("EngName")
+                        .IsRequired()
+                        .HasMaxLength(120)
+                        .HasColumnType("nvarchar(120)")
+                        .HasComment("영문 명");
+
+                    b.Property<string>("Image")
+                        .HasMaxLength(1000)
+                        .HasColumnType("nvarchar(1000)")
+                        .HasComment("이미지");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<string>("KorName")
+                        .IsRequired()
+                        .HasMaxLength(240)
+                        .HasColumnType("nvarchar(240)")
+                        .HasComment("한글 명");
+
+                    b.Property<short>("Order")
+                        .HasColumnType("smallint")
+                        .HasComment("순서");
+
+                    b.Property<long>("RequiredAttendance")
+                        .HasColumnType("bigint")
+                        .HasComment("누적 출석 수");
+
+                    b.Property<int>("RequiredExp")
+                        .HasColumnType("int")
+                        .HasComment("누적 경험치");
+
+                    b.Property<string>("TextColor")
+                        .HasMaxLength(7)
+                        .HasColumnType("nvarchar(7)")
+                        .HasComment("표시 색상");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("EngName")
+                        .IsUnique();
+
+                    b.HasIndex("IsActive");
+
+                    b.HasIndex("KorName")
+                        .IsUnique();
+
+                    b.HasIndex("Order");
+
+                    b.HasIndex("Order", "IsActive");
+
+                    b.ToTable("MemberGrade", null, t =>
+                        {
+                            t.HasComment("회원 등급");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.MemberStats", b =>
+                {
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int")
+                        .HasComment("회원 ID");
+
+                    b.Property<long>("AttendanceCount")
+                        .HasColumnType("bigint")
+                        .HasComment("출석");
+
+                    b.Property<long>("BookmarkGivenCount")
+                        .HasColumnType("bigint")
+                        .HasComment("즐겨찾기 글 수");
+
+                    b.Property<long>("CommentCount")
+                        .HasColumnType("bigint")
+                        .HasComment("작성 댓글");
+
+                    b.Property<long>("Exp")
+                        .HasColumnType("bigint")
+                        .HasComment("경험치");
+
+                    b.Property<long>("FollowerCount")
+                        .HasColumnType("bigint")
+                        .HasComment("구독자");
+
+                    b.Property<long>("FollowingCount")
+                        .HasColumnType("bigint")
+                        .HasComment("구독 중");
+
+                    b.Property<long>("LikeGivenCount")
+                        .HasColumnType("bigint")
+                        .HasComment("누른 좋아요 수");
+
+                    b.Property<long>("LikeReceivedCount")
+                        .HasColumnType("bigint")
+                        .HasComment("받은 좋아요 수");
+
+                    b.Property<long>("LoginCount")
+                        .HasColumnType("bigint")
+                        .HasComment("로그인");
+
+                    b.Property<long>("PaymentCount")
+                        .HasColumnType("bigint")
+                        .HasComment("결제 횟수");
+
+                    b.Property<long>("PostCount")
+                        .HasColumnType("bigint")
+                        .HasComment("작성 게시글");
+
+                    b.Property<long>("ReportedCount")
+                        .HasColumnType("bigint")
+                        .HasComment("신고 당한 횟수");
+
+                    b.Property<byte[]>("RowVersion")
+                        .IsConcurrencyToken()
+                        .IsRequired()
+                        .ValueGeneratedOnAddOrUpdate()
+                        .HasColumnType("rowversion")
+                        .HasComment("동시성");
+
+                    b.Property<int>("SuspensionCount")
+                        .HasColumnType("int")
+                        .HasComment("정지 횟수");
+
+                    b.Property<long>("TotalCanceledAmount")
+                        .HasColumnType("bigint")
+                        .HasComment("누적 취소/환불 금액");
+
+                    b.Property<long>("TotalPaidAmount")
+                        .HasColumnType("bigint")
+                        .HasComment("누적 결제 금액");
+
+                    b.Property<int>("WarningCount")
+                        .HasColumnType("int")
+                        .HasComment("경고 횟수");
+
+                    b.HasKey("MemberID");
+
+                    b.ToTable("MemberStats", null, t =>
+                        {
+                            t.HasComment("회원 활동 집계");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Banner.BannerItem", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<string>("DesktopImage")
+                        .HasMaxLength(1024)
+                        .HasColumnType("nvarchar(1024)")
+                        .HasComment("이미지(Desktop)");
+
+                    b.Property<DateTime?>("EndAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("사용 기간 - 종료");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<string>("Link")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("주소");
+
+                    b.Property<string>("MobileImage")
+                        .HasMaxLength(1024)
+                        .HasColumnType("nvarchar(1024)")
+                        .HasComment("이미지(Mobile)");
+
+                    b.Property<short>("Order")
+                        .HasColumnType("smallint")
+                        .HasComment("순서");
+
+                    b.Property<int>("PositionID")
+                        .HasColumnType("int")
+                        .HasComment("배너 위치 ID");
+
+                    b.Property<DateTime?>("StartAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("사용 기간 - 시작");
+
+                    b.Property<string>("Subject")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("배너 명");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("IsActive");
+
+                    b.HasIndex("Order");
+
+                    b.HasIndex("PositionID");
+
+                    b.HasIndex("PositionID", "Order", "IsActive");
+
+                    b.ToTable("BannerItem", null, t =>
+                        {
+                            t.HasComment("배너 아이템");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Banner.BannerPosition", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasMaxLength(30)
+                        .HasColumnType("nvarchar(30)")
+                        .HasComment("위치 구분");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<string>("Subject")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("위치 명");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Code")
+                        .IsUnique();
+
+                    b.HasIndex("IsActive");
+
+                    b.HasIndex("Code", "IsActive");
+
+                    b.ToTable("BannerPosition", null, t =>
+                        {
+                            t.HasComment("배너 위치");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Document", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasMaxLength(30)
+                        .HasColumnType("nvarchar(30)")
+                        .HasComment("주소");
+
+                    b.Property<string>("Content")
+                        .HasMaxLength(5000)
+                        .HasColumnType("nvarchar(max)")
+                        .HasComment("내용");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<string>("Subject")
+                        .IsRequired()
+                        .HasMaxLength(120)
+                        .HasColumnType("nvarchar(120)")
+                        .HasComment("제목");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.Property<int>("Views")
+                        .HasColumnType("int")
+                        .HasComment("조회 수");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Code")
+                        .IsUnique();
+
+                    b.HasIndex("IsActive");
+
+                    b.HasIndex("Subject");
+
+                    b.HasIndex("Code", "IsActive");
+
+                    b.ToTable("Document", null, t =>
+                        {
+                            t.HasComment("문서");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Faq.FaqCategory", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasMaxLength(30)
+                        .HasColumnType("nvarchar(30)")
+                        .HasComment("주소");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<short>("Order")
+                        .HasColumnType("smallint")
+                        .HasComment("순서");
+
+                    b.Property<string>("Subject")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("분류 명");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Code")
+                        .IsUnique();
+
+                    b.HasIndex("Order", "IsActive");
+
+                    b.HasIndex("Code", "Order", "IsActive");
+
+                    b.ToTable("FaqCategory", null, t =>
+                        {
+                            t.HasComment("FAQ 분류");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Faq.FaqItem", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Answer")
+                        .HasMaxLength(4000)
+                        .HasColumnType("nvarchar(4000)")
+                        .HasComment("답변");
+
+                    b.Property<int>("CategoryID")
+                        .HasColumnType("int")
+                        .HasComment("분류 ID");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<short>("Order")
+                        .HasColumnType("smallint")
+                        .HasComment("순서");
+
+                    b.Property<string>("Question")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("질문");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("CategoryID");
+
+                    b.HasIndex("IsActive");
+
+                    b.HasIndex("Order");
+
+                    b.HasIndex("Order", "IsActive");
+
+                    b.HasIndex("CategoryID", "Order", "IsActive");
+
+                    b.ToTable("FaqItem", null, t =>
+                        {
+                            t.HasComment("FAQ 목록");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Popup", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int")
+                        .HasComment("PK");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<string>("Content")
+                        .HasMaxLength(4000)
+                        .HasColumnType("nvarchar(4000)")
+                        .HasComment("내용");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("등록 일시");
+
+                    b.Property<DateTime?>("EndAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("사용 기간 - 종료");
+
+                    b.Property<bool>("IsActive")
+                        .HasColumnType("bit")
+                        .HasComment("사용 여부");
+
+                    b.Property<string>("Link")
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("주소");
+
+                    b.Property<short>("Order")
+                        .HasColumnType("smallint")
+                        .HasComment("순서");
+
+                    b.Property<DateTime?>("StartAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("사용 기간 - 시작");
+
+                    b.Property<string>("Subject")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("nvarchar(255)")
+                        .HasComment("제목");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2")
+                        .HasComment("수정 일시");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("Order");
+
+                    b.HasIndex("Order", "IsActive");
+
+                    b.HasIndex("StartAt", "EndAt", "Order", "IsActive");
+
+                    b.ToTable("Popup", null, t =>
+                        {
+                            t.HasComment("팝업");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.Wallet", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2");
+
+                    b.Property<int>("MemberID")
+                        .HasColumnType("int");
+
+                    b.Property<DateTime?>("UpdatedAt")
+                        .HasColumnType("datetime2");
+
+                    b.Property<Guid>("WalletKey")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("MemberID")
+                        .IsUnique();
+
+                    b.HasIndex("WalletKey")
+                        .IsUnique();
+
+                    b.ToTable("Wallet", null, t =>
+                        {
+                            t.HasComment("회원 지갑");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.WalletBalance", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.Property<Guid>("WalletKey")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("WalletKey", "Type")
+                        .IsUnique();
+
+                    b.ToTable("WalletBalance", null, t =>
+                        {
+                            t.HasComment("회원 지갑 잔액");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.WalletTransaction", b =>
+                {
+                    b.Property<int>("ID")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ID"));
+
+                    b.Property<int>("BalanceType")
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime2");
+
+                    b.Property<string>("Memo")
+                        .HasMaxLength(500)
+                        .HasColumnType("nvarchar(500)");
+
+                    b.Property<string>("Reason")
+                        .IsRequired()
+                        .HasMaxLength(1000)
+                        .HasColumnType("nvarchar(1000)");
+
+                    b.Property<string>("RefID")
+                        .HasMaxLength(100)
+                        .HasColumnType("nvarchar(100)");
+
+                    b.Property<int>("TxType")
+                        .HasColumnType("int");
+
+                    b.Property<string>("UserID")
+                        .HasMaxLength(100)
+                        .HasColumnType("nvarchar(100)");
+
+                    b.Property<Guid>("WalletKey")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("ID");
+
+                    b.HasIndex("CreatedAt");
+
+                    b.HasIndex("WalletKey");
+
+                    b.HasIndex("WalletKey", "CreatedAt");
+
+                    b.ToTable("WalletTransaction", null, t =>
+                        {
+                            t.HasComment("회원 거래 장부");
+                        });
+                });
+
+            modelBuilder.Entity("Domain.Entities.Common.Config", b =>
+                {
+                    b.OwnsOne("Domain.Entities.Common.AccountConfig", "Account", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<int?>("ChangeEmailDay")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_ChangeEmailDay")
+                                .HasComment("이메일 갱신 주기(일)");
+
+                            b1.Property<int?>("ChangeIntroDay")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_ChangeIntroDay")
+                                .HasComment("자기소개 갱신 주기(일)");
+
+                            b1.Property<int?>("ChangeNameDay")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_ChangeNameDay")
+                                .HasComment("별명 갱신 주기(일)");
+
+                            b1.Property<int?>("ChangePasswordDay")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_ChangePasswordDay")
+                                .HasComment("비밀번호 갱신 주기(일)");
+
+                            b1.Property<int?>("ChangeSummaryDay")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_ChangeSummaryDay")
+                                .HasComment("한마디 갱신 주기(일)");
+
+                            b1.Property<string>("DeniedEmailList")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("Account_DeniedEmailList")
+                                .HasComment("금지 이메일");
+
+                            b1.Property<string>("DeniedNameList")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("Account_DeniedNameList")
+                                .HasComment("금지 별명");
+
+                            b1.Property<bool>("IsRegisterBlock")
+                                .HasColumnType("bit")
+                                .HasColumnName("Account_IsRegisterBlock")
+                                .HasComment("회원가입 차단");
+
+                            b1.Property<bool>("IsRegisterEmailAuth")
+                                .HasColumnType("bit")
+                                .HasColumnName("Account_IsRegisterEmailAuth")
+                                .HasComment("회원가입 시 이메일 인증");
+
+                            b1.Property<int?>("MaxLoginTryCount")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_MaxLoginTryCount")
+                                .HasComment("로그인 시도 제한 횟수");
+
+                            b1.Property<int?>("MaxLoginTryLimitSecond")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_MaxLoginTryLimitSecond")
+                                .HasComment("로그인 시도 제한 시간(초)");
+
+                            b1.Property<int?>("PasswordMinLength")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_PasswordMinLength")
+                                .HasComment("비밀번호 최소 길이");
+
+                            b1.Property<int?>("PasswordNumbersLength")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_PasswordNumbersLength")
+                                .HasComment("비밀번호 최소 숫자 수");
+
+                            b1.Property<int?>("PasswordSpecialcharsLength")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_PasswordSpecialcharsLength")
+                                .HasComment("비밀번호 최소 특수문자 수");
+
+                            b1.Property<int?>("PasswordUppercaseLength")
+                                .HasColumnType("int")
+                                .HasColumnName("Account_PasswordUppercaseLength")
+                                .HasComment("비밀번호 최소 대문자 수");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.BasicConfig", "Basic", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("AdminWhiteIPList")
+                                .HasMaxLength(1000)
+                                .HasColumnType("nvarchar(1000)")
+                                .HasColumnName("Basic_AdminWhiteIPList")
+                                .HasComment("관리자단 접근 가능 IP");
+
+                            b1.Property<string>("BlockAlertContent")
+                                .HasMaxLength(5000)
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("Basic_BlockAlertContent")
+                                .HasComment("차단 시 안내문 내용");
+
+                            b1.Property<string>("BlockAlertTitle")
+                                .HasMaxLength(200)
+                                .HasColumnType("nvarchar(200)")
+                                .HasColumnName("Basic_BlockAlertTitle")
+                                .HasComment("차단 시 안내문 제목");
+
+                            b1.Property<string>("FromEmail")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Basic_FromEmail")
+                                .HasComment("송수신 이메일");
+
+                            b1.Property<string>("FromName")
+                                .HasMaxLength(30)
+                                .HasColumnType("nvarchar(30)")
+                                .HasColumnName("Basic_FromName")
+                                .HasComment("송수신자 이름");
+
+                            b1.Property<string>("FrontWhiteIPList")
+                                .HasMaxLength(1000)
+                                .HasColumnType("nvarchar(1000)")
+                                .HasColumnName("Basic_FrontWhiteIPList")
+                                .HasComment("사용자단 접근 가능 IP");
+
+                            b1.Property<bool>("IsMaintenance")
+                                .HasColumnType("bit")
+                                .HasColumnName("Basic_IsMaintenance")
+                                .HasComment("점검 여부");
+
+                            b1.Property<string>("MaintenanceContent")
+                                .HasMaxLength(5000)
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("Basic_MaintenanceContent")
+                                .HasComment("점검 내용");
+
+                            b1.Property<string>("RootID")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Basic_RootID")
+                                .HasComment("최고 관리자 ID");
+
+                            b1.Property<string>("SiteName")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Basic_SiteName")
+                                .HasComment("사이트 이름");
+
+                            b1.Property<string>("SiteURL")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Basic_SiteURL")
+                                .HasComment("사이트 주소");
+
+                            b1.Property<bool>("SmtpEnableSSL")
+                                .HasColumnType("bit")
+                                .HasColumnName("Basic_SmtpEnableSSL")
+                                .HasComment("SMTP Enable SSL");
+
+                            b1.Property<string>("SmtpPassword")
+                                .HasMaxLength(200)
+                                .HasColumnType("nvarchar(200)")
+                                .HasColumnName("Basic_SmtpPassword")
+                                .HasComment("SMTP Password");
+
+                            b1.Property<int?>("SmtpPort")
+                                .HasColumnType("int")
+                                .HasColumnName("Basic_SmtpPort")
+                                .HasComment("SMTP Port");
+
+                            b1.Property<string>("SmtpServer")
+                                .HasMaxLength(200)
+                                .HasColumnType("nvarchar(200)")
+                                .HasColumnName("Basic_SmtpServer")
+                                .HasComment("SMTP Server");
+
+                            b1.Property<string>("SmtpUsername")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Basic_SmtpUsername")
+                                .HasComment("SMTP Username");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.CompanyConfig", "Company", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("AddedSaleNo")
+                                .HasMaxLength(20)
+                                .HasColumnType("nvarchar(20)")
+                                .HasColumnName("Company_AddedSaleNo")
+                                .HasComment("부가통신 사업자번호");
+
+                            b1.Property<string>("Address")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Company_Address")
+                                .HasComment("사업장 소재지");
+
+                            b1.Property<string>("AdminEmail")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Company_AdminEmail")
+                                .HasComment("정보관리책임자 이메일");
+
+                            b1.Property<string>("AdminName")
+                                .HasMaxLength(70)
+                                .HasColumnType("nvarchar(70)")
+                                .HasColumnName("Company_AdminName")
+                                .HasComment("정보관리책임자");
+
+                            b1.Property<string>("BankCode")
+                                .HasMaxLength(10)
+                                .HasColumnType("nvarchar(10)")
+                                .HasColumnName("Company_BankCode")
+                                .HasComment("입금계좌 - 은행");
+
+                            b1.Property<string>("BankNumber")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Company_BankNumber")
+                                .HasComment("입금계좌 - 계좌번호");
+
+                            b1.Property<string>("BankOwner")
+                                .HasMaxLength(70)
+                                .HasColumnType("nvarchar(70)")
+                                .HasColumnName("Company_BankOwner")
+                                .HasComment("입금계좌 - 예금주");
+
+                            b1.Property<string>("Fax")
+                                .HasMaxLength(20)
+                                .HasColumnType("nvarchar(20)")
+                                .HasColumnName("Company_Fax")
+                                .HasComment("FAX");
+
+                            b1.Property<string>("Hosting")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Company_Hosting")
+                                .HasComment("호스팅 서비스");
+
+                            b1.Property<string>("Name")
+                                .HasMaxLength(70)
+                                .HasColumnType("nvarchar(70)")
+                                .HasColumnName("Company_Name")
+                                .HasComment("상호 명");
+
+                            b1.Property<string>("Owner")
+                                .HasMaxLength(50)
+                                .HasColumnType("nvarchar(50)")
+                                .HasColumnName("Company_Owner")
+                                .HasComment("대표자 명");
+
+                            b1.Property<string>("RegNo")
+                                .HasMaxLength(100)
+                                .HasColumnType("nvarchar(100)")
+                                .HasColumnName("Company_RegNo")
+                                .HasComment("사업자 등록 번호");
+
+                            b1.Property<string>("RetailSaleNo")
+                                .HasMaxLength(20)
+                                .HasColumnType("nvarchar(20)")
+                                .HasColumnName("Company_RetailSaleNo")
+                                .HasComment("통신판매업 신고번호");
+
+                            b1.Property<string>("SiteUrl")
+                                .HasMaxLength(200)
+                                .HasColumnType("nvarchar(200)")
+                                .HasColumnName("Company_SiteUrl")
+                                .HasComment("사이트 주소");
+
+                            b1.Property<string>("Tel")
+                                .HasMaxLength(20)
+                                .HasColumnType("nvarchar(20)")
+                                .HasColumnName("Company_Tel")
+                                .HasComment("대표 전화번호");
+
+                            b1.Property<string>("ZipCode")
+                                .HasMaxLength(8)
+                                .HasColumnType("nvarchar(8)")
+                                .HasColumnName("Company_ZipCode")
+                                .HasComment("사업장 주소(우편번호)");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.EmailTemplateConfig", "EmailTemplate", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("ChangedEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ChangedEmailFormContent")
+                                .HasComment("이메일 변경 완료 - 내용");
+
+                            b1.Property<string>("ChangedEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ChangedEmailFormTitle")
+                                .HasComment("이메일 변경 완료 - 제목");
+
+                            b1.Property<string>("ChangedPasswordEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ChangedPasswordEmailFormContent")
+                                .HasComment("비밀번호 변경 완료 - 내용");
+
+                            b1.Property<string>("ChangedPasswordEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ChangedPasswordEmailFormTitle")
+                                .HasComment("비밀번호 변경 완료 - 제목");
+
+                            b1.Property<string>("EmailVerifyFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_EmailVerifyFormContent")
+                                .HasComment("이메일 변경 시 - 내용");
+
+                            b1.Property<string>("EmailVerifyFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_EmailVerifyFormTitle")
+                                .HasComment("이메일 변경 시 - 제목");
+
+                            b1.Property<string>("RegisterEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_RegisterEmailFormContent")
+                                .HasComment("회원가입 시 - 내용");
+
+                            b1.Property<string>("RegisterEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_RegisterEmailFormTitle")
+                                .HasComment("회원가입 시 - 제목");
+
+                            b1.Property<string>("RegistrationEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_RegistrationEmailFormContent")
+                                .HasComment("회원가입 완료 - 내용");
+
+                            b1.Property<string>("RegistrationEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_RegistrationEmailFormTitle")
+                                .HasComment("회원가입 완료 - 제목");
+
+                            b1.Property<string>("ResetPasswordEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ResetPasswordEmailFormContent")
+                                .HasComment("비밀번호 재설정 - 내용");
+
+                            b1.Property<string>("ResetPasswordEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_ResetPasswordEmailFormTitle")
+                                .HasComment("비밀번호 재설정 - 제목");
+
+                            b1.Property<string>("WithdrawEmailFormContent")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_WithdrawEmailFormContent")
+                                .HasComment("회원탈퇴 시 - 내용");
+
+                            b1.Property<string>("WithdrawEmailFormTitle")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("EmailTemplate_WithdrawEmailFormTitle")
+                                .HasComment("회원탈퇴 시 - 제목");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.ExternalApiConfig", "External", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("GoogleAppId")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("External_GoogleAppId")
+                                .HasComment("Google APP ID");
+
+                            b1.Property<string>("GoogleClientId")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("External_GoogleClientId")
+                                .HasComment("Google Client ID");
+
+                            b1.Property<string>("GoogleClientSecretEnc")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("External_GoogleClientSecretEnc")
+                                .HasComment("Google Client Secret (암호화 저장 권장)");
+
+                            b1.Property<string>("YouTubeApiKeyEnc")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("External_YouTubeApiKeyEnc")
+                                .HasComment("YouTube API Key (암호화 저장 권장)");
+
+                            b1.Property<string>("YouTubeApiName")
+                                .HasColumnType("nvarchar(max)")
+                                .HasColumnName("External_YouTubeApiName")
+                                .HasComment("YouTube API Name");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.ImagesConfig", "Images", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("AppIcon_192")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_AppIcon_192")
+                                .HasComment("App-icon-192");
+
+                            b1.Property<string>("AppIcon_512")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_AppIcon_512")
+                                .HasComment("App-icon-512");
+
+                            b1.Property<string>("AppleTouchIcon")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_AppleTouchIcon")
+                                .HasComment("Apple-touch-icon");
+
+                            b1.Property<string>("Favicon")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_Favicon")
+                                .HasComment("Favicon");
+
+                            b1.Property<string>("LogoHorizontal")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_LogoHorizontal")
+                                .HasComment("Logo-horizontal");
+
+                            b1.Property<string>("LogoSquare")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_LogoSquare")
+                                .HasComment("Logo-square");
+
+                            b1.Property<string>("OgDefault")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_OgDefault")
+                                .HasComment("og-default");
+
+                            b1.Property<string>("TwitterImage")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Images_TwitterImage")
+                                .HasComment("Twitter-image");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.MetaConfig", "Meta", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("Adds")
+                                .HasColumnType("nvarchar(max)");
+
+                            b1.Property<string>("ApplicationName")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_ApplicationName")
+                                .HasComment("Meta Application Name");
+
+                            b1.Property<string>("Author")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Author")
+                                .HasComment("Meta Author");
+
+                            b1.Property<string>("Description")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Description")
+                                .HasComment("Meta Description");
+
+                            b1.Property<string>("Generator")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Generator")
+                                .HasComment("Meta Generator");
+
+                            b1.Property<string>("Keywords")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Keywords")
+                                .HasComment("Meta Keywords");
+
+                            b1.Property<string>("Robots")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Robots")
+                                .HasComment("Meta Robots");
+
+                            b1.Property<string>("Viewport")
+                                .HasMaxLength(255)
+                                .HasColumnType("nvarchar(255)")
+                                .HasColumnName("Meta_Viewport")
+                                .HasComment("Meta Viewport");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.PaymentConfig", "Payment", b1 =>
+                        {
+                            b1.Property<int>("ConfigID")
+                                .HasColumnType("int");
+
+                            b1.HasKey("ConfigID");
+
+                            b1.ToTable("Config");
+
+                            b1.WithOwner()
+                                .HasForeignKey("ConfigID");
+                        });
+
+                    b.Navigation("Account")
+                        .IsRequired();
+
+                    b.Navigation("Basic")
+                        .IsRequired();
+
+                    b.Navigation("Company")
+                        .IsRequired();
+
+                    b.Navigation("EmailTemplate")
+                        .IsRequired();
+
+                    b.Navigation("External")
+                        .IsRequired();
+
+                    b.Navigation("Images")
+                        .IsRequired();
+
+                    b.Navigation("Meta")
+                        .IsRequired();
+
+                    b.Navigation("Payment")
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Channel", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithOne("Channel")
+                        .HasForeignKey("Domain.Entities.Members.Channel", "MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberEmailChangeLog", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithMany()
+                        .HasForeignKey("MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberIntroChangeLog", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithMany()
+                        .HasForeignKey("MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberLoginLog", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithMany()
+                        .HasForeignKey("MemberID")
+                        .OnDelete(DeleteBehavior.SetNull);
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberNameChangeLog", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithMany()
+                        .HasForeignKey("MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Logs.MemberSummaryChangeLog", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithMany()
+                        .HasForeignKey("MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Member", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.MemberGrade", "MemberGrade")
+                        .WithMany()
+                        .HasForeignKey("MemberGradeID")
+                        .OnDelete(DeleteBehavior.SetNull);
+
+                    b.Navigation("MemberGrade");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.MemberApprove", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithOne("MemberApprove")
+                        .HasForeignKey("Domain.Entities.Members.MemberApprove", "MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.MemberStats", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithOne("MemberStats")
+                        .HasForeignKey("Domain.Entities.Members.MemberStats", "MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Banner.BannerItem", b =>
+                {
+                    b.HasOne("Domain.Entities.Page.Banner.BannerPosition", "BannerPosition")
+                        .WithMany("BannerItems")
+                        .HasForeignKey("PositionID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("BannerPosition");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Faq.FaqItem", b =>
+                {
+                    b.HasOne("Domain.Entities.Page.Faq.FaqCategory", "FaqCategory")
+                        .WithMany("FaqItems")
+                        .HasForeignKey("CategoryID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("FaqCategory");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.Wallet", b =>
+                {
+                    b.HasOne("Domain.Entities.Members.Member", "Member")
+                        .WithOne("Wallet")
+                        .HasForeignKey("Domain.Entities.Wallets.Wallet", "MemberID")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Member");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.WalletBalance", b =>
+                {
+                    b.HasOne("Domain.Entities.Wallets.Wallet", null)
+                        .WithMany("Balances")
+                        .HasForeignKey("WalletKey")
+                        .HasPrincipalKey("WalletKey")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.OwnsOne("Domain.Entities.Common.ValueObject.Money", "Amount", b1 =>
+                        {
+                            b1.Property<int>("WalletBalanceID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("Currency")
+                                .IsRequired()
+                                .HasMaxLength(10)
+                                .HasColumnType("nvarchar(10)")
+                                .HasColumnName("Currency");
+
+                            b1.Property<decimal>("Value")
+                                .HasPrecision(18)
+                                .HasColumnType("decimal(18,0)")
+                                .HasColumnName("Amount");
+
+                            b1.HasKey("WalletBalanceID");
+
+                            b1.ToTable("WalletBalance");
+
+                            b1.WithOwner()
+                                .HasForeignKey("WalletBalanceID");
+                        });
+
+                    b.Navigation("Amount")
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.WalletTransaction", b =>
+                {
+                    b.HasOne("Domain.Entities.Wallets.Wallet", "Wallet")
+                        .WithMany("Transactions")
+                        .HasForeignKey("WalletKey")
+                        .HasPrincipalKey("WalletKey")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.OwnsOne("Domain.Entities.Common.ValueObject.Money", "Amount", b1 =>
+                        {
+                            b1.Property<int>("WalletTransactionID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("Currency")
+                                .IsRequired()
+                                .HasMaxLength(10)
+                                .HasColumnType("nvarchar(10)")
+                                .HasColumnName("Currency");
+
+                            b1.Property<decimal>("Value")
+                                .HasPrecision(18)
+                                .HasColumnType("decimal(18,0)")
+                                .HasColumnName("Amount");
+
+                            b1.HasKey("WalletTransactionID");
+
+                            b1.ToTable("WalletTransaction");
+
+                            b1.WithOwner()
+                                .HasForeignKey("WalletTransactionID");
+                        });
+
+                    b.OwnsOne("Domain.Entities.Common.ValueObject.Money", "BalanceAfter", b1 =>
+                        {
+                            b1.Property<int>("WalletTransactionID")
+                                .HasColumnType("int");
+
+                            b1.Property<string>("Currency")
+                                .IsRequired()
+                                .HasMaxLength(10)
+                                .HasColumnType("nvarchar(10)")
+                                .HasColumnName("BalanceAfterCurrency");
+
+                            b1.Property<decimal>("Value")
+                                .HasPrecision(18)
+                                .HasColumnType("decimal(18,0)")
+                                .HasColumnName("BalanceAfter");
+
+                            b1.HasKey("WalletTransactionID");
+
+                            b1.ToTable("WalletTransaction");
+
+                            b1.WithOwner()
+                                .HasForeignKey("WalletTransactionID");
+                        });
+
+                    b.Navigation("Amount")
+                        .IsRequired();
+
+                    b.Navigation("BalanceAfter")
+                        .IsRequired();
+
+                    b.Navigation("Wallet");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Members.Member", b =>
+                {
+                    b.Navigation("Channel");
+
+                    b.Navigation("MemberApprove")
+                        .IsRequired();
+
+                    b.Navigation("MemberStats")
+                        .IsRequired();
+
+                    b.Navigation("Wallet");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Banner.BannerPosition", b =>
+                {
+                    b.Navigation("BannerItems");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Page.Faq.FaqCategory", b =>
+                {
+                    b.Navigation("FaqItems");
+                });
+
+            modelBuilder.Entity("Domain.Entities.Wallets.Wallet", b =>
+                {
+                    b.Navigation("Balances");
+
+                    b.Navigation("Transactions");
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}

+ 30 - 0
Infrastructure/Infrastructure/Persistence/Migrations/20260205091913_a3.cs

@@ -0,0 +1,30 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Infrastructure.Infrastructure.Persistence.Migrations
+{
+    /// <inheritdoc />
+    public partial class a3 : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.AddColumn<string>(
+                name: "TextColor",
+                table: "MemberGrade",
+                type: "nvarchar(7)",
+                maxLength: 7,
+                nullable: true,
+                comment: "표시 색상");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropColumn(
+                name: "TextColor",
+                table: "MemberGrade");
+        }
+    }
+}

+ 2 - 0
Infrastructure/Persistence/AppDbContext.cs

@@ -1,5 +1,6 @@
 using Application.Abstractions.Data;
 using Domain.Entities.Common;
+using Domain.Entities.Members;
 using Domain.Entities.Page;
 using Domain.Entities.Page.Banner;
 using Domain.Entities.Page.Faq;
@@ -18,6 +19,7 @@ namespace Infrastructure.Persistence
         public DbSet<FaqItem> FaqItem { get; set; } = null!;
         public DbSet<BannerPosition> BannerPosition { get; set; } = null!;
         public DbSet<BannerItem> BannerItem { get; set; } = null!;
+        public DbSet<MemberGrade> MemberGrade { get; set; } = null!;
 
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {

+ 13 - 12
Infrastructure/Persistence/Configurations/Members/MemberGradeConfiguration.cs

@@ -14,18 +14,19 @@ public sealed class MemberGradeConfiguration : IEntityTypeConfiguration<MemberGr
         builder.HasIndex(x => x.IsActive);
         builder.HasIndex(x => new { x.Order, x.IsActive });
 
-        builder.ToTable(nameof(MemberGrade), x => x.HasComment("ȸ¿ø µî±Þ"));
+        builder.ToTable(nameof(MemberGrade), x => x.HasComment("회� 등급"));
         builder.HasKey(x => x.ID);
         builder.Property(x => x.ID).ValueGeneratedOnAdd().HasComment("PK");
-        builder.Property(x => x.KorName).HasMaxLength(240).IsRequired().HasComment("ÇÑ±Û ¸í");
-        builder.Property(x => x.EngName).HasMaxLength(120).IsRequired().HasComment("¿µ¹® ¸í");
-        builder.Property(x => x.Description).HasMaxLength(1000).HasComment("¼³¸í");
-        builder.Property(x => x.Order).IsRequired().HasComment("¼ø¼­");
-        builder.Property(x => x.Image).HasMaxLength(1000).HasComment("À̹ÌÁö");
-        builder.Property(x => x.RequiredExp).IsRequired().HasComment("´©Àû °æÇèÄ¡");
-        builder.Property(x => x.RequiredAttendance).IsRequired().HasComment("´©Àû Ãâ¼® ¼ö");
-        builder.Property(x => x.IsActive).IsRequired().HasComment("»ç¿ë ¿©ºÎ");
-        builder.Property(x => x.UpdatedAt).HasComment("¼öÁ¤ ÀϽÃ");
-        builder.Property(x => x.CreatedAt).IsRequired().HasComment("µî·Ï ÀϽÃ");
+        builder.Property(x => x.KorName).HasMaxLength(240).IsRequired().HasComment("한글 명");
+        builder.Property(x => x.EngName).HasMaxLength(120).IsRequired().HasComment("�문 명");
+        builder.Property(x => x.Description).HasMaxLength(1000).HasComment("설명");
+        builder.Property(x => x.Order).IsRequired().HasComment("순서");
+        builder.Property(x => x.Image).HasMaxLength(1000).HasComment("�미지");
+        builder.Property(x => x.TextColor).HasMaxLength(7).HasComment("표시 색�");
+        builder.Property(x => x.RequiredExp).IsRequired().HasComment("누� 경험치");
+        builder.Property(x => x.RequiredAttendance).IsRequired().HasComment("누� 출� 수");
+        builder.Property(x => x.IsActive).IsRequired().HasComment("사용 여부");
+        builder.Property(x => x.UpdatedAt).HasComment("수정 �시");
+        builder.Property(x => x.CreatedAt).IsRequired().HasComment("등� �시");
     }
-}
+}

+ 5 - 0
Infrastructure/Persistence/Migrations/AppDbContextModelSnapshot.cs

@@ -849,6 +849,11 @@ namespace Infrastructure.Persistence.Migrations
                         .HasColumnType("int")
                         .HasComment("누적 경험치");
 
+                    b.Property<string>("TextColor")
+                        .HasMaxLength(7)
+                        .HasColumnType("nvarchar(7)")
+                        .HasComment("표시 색상");
+
                     b.Property<DateTime?>("UpdatedAt")
                         .HasColumnType("datetime2")
                         .HasComment("수정 일시");

+ 0 - 0
Admin/SharedKernel/Extensions/ModelStateDictionaryExtensions.cs → SharedKernel/Extensions/ModelStateDictionaryExtensions.cs


+ 1 - 0
SharedKernel/SharedKernel.csproj

@@ -9,6 +9,7 @@
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="10.0.2" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.9" />
   </ItemGroup>
 
 </Project>