浏览代码

no message

KIM-JINO5 4 月之前
父节点
当前提交
21b739e173
共有 54 个文件被更改,包括 6957 次插入12 次删除
  1. 8 3
      .claude/settings.local.json
  2. 0 0
      Admin/Application/Features/Banner/Item/Delete/Command.cs
  3. 0 0
      Admin/Application/Features/Banner/Item/Delete/Handler.cs
  4. 0 0
      Admin/Application/Features/Banner/Item/Update/Handler.cs
  5. 0 0
      Admin/Application/Features/Banner/Position/Save/Handler.cs
  6. 150 0
      Admin/Pages/Member/Log/Email.cshtml
  7. 108 0
      Admin/Pages/Member/Log/Email.cshtml.cs
  8. 203 0
      Admin/Pages/Member/Log/Intro.cshtml
  9. 104 0
      Admin/Pages/Member/Log/Intro.cshtml.cs
  10. 153 0
      Admin/Pages/Member/Log/Login/Index.cshtml
  11. 119 0
      Admin/Pages/Member/Log/Login/Index.cshtml.cs
  12. 91 0
      Admin/Pages/Member/Log/Login/Info.cshtml
  13. 18 0
      Admin/Pages/Member/Log/Login/Info.cshtml.cs
  14. 150 0
      Admin/Pages/Member/Log/Name.cshtml
  15. 108 0
      Admin/Pages/Member/Log/Name.cshtml.cs
  16. 144 0
      Admin/Pages/Member/Log/Summary.cshtml
  17. 104 0
      Admin/Pages/Member/Log/Summary.cshtml.cs
  18. 20 0
      Admin/Pages/Member/_navTabs.cshtml
  19. 21 1
      Admin/using.cs
  20. 9 0
      Application/Abstractions/Data/IAppDbContext.cs
  21. 6 0
      Application/Features/Member/EmailChangeLog/Delete/Command.cs
  22. 18 0
      Application/Features/Member/EmailChangeLog/Delete/Handler.cs
  23. 75 0
      Application/Features/Member/EmailChangeLog/Search/Handler.cs
  24. 13 0
      Application/Features/Member/EmailChangeLog/Search/Query.cs
  25. 20 0
      Application/Features/Member/EmailChangeLog/Search/Response.cs
  26. 5 0
      Application/Features/Member/IntroChangeLog/Delete/Command.cs
  27. 18 0
      Application/Features/Member/IntroChangeLog/Delete/Handler.cs
  28. 78 0
      Application/Features/Member/IntroChangeLog/Search/Handler.cs
  29. 13 0
      Application/Features/Member/IntroChangeLog/Search/Query.cs
  30. 21 0
      Application/Features/Member/IntroChangeLog/Search/Response.cs
  31. 6 0
      Application/Features/Member/LoginLog/Delete/Command.cs
  32. 18 0
      Application/Features/Member/LoginLog/Delete/Handler.cs
  33. 94 0
      Application/Features/Member/LoginLog/Get/Handler.cs
  34. 6 0
      Application/Features/Member/LoginLog/Get/Query.cs
  35. 19 0
      Application/Features/Member/LoginLog/Get/Response.cs
  36. 125 0
      Application/Features/Member/LoginLog/Search/Handler.cs
  37. 13 0
      Application/Features/Member/LoginLog/Search/Query.cs
  38. 25 0
      Application/Features/Member/LoginLog/Search/Response.cs
  39. 6 0
      Application/Features/Member/NameChangeLog/Delete/Command.cs
  40. 18 0
      Application/Features/Member/NameChangeLog/Delete/Handler.cs
  41. 74 0
      Application/Features/Member/NameChangeLog/Search/Handler.cs
  42. 13 0
      Application/Features/Member/NameChangeLog/Search/Query.cs
  43. 20 0
      Application/Features/Member/NameChangeLog/Search/Response.cs
  44. 6 0
      Application/Features/Member/SummaryChangeLog/Delete/Command.cs
  45. 18 0
      Application/Features/Member/SummaryChangeLog/Delete/Handler.cs
  46. 74 0
      Application/Features/Member/SummaryChangeLog/Search/Handler.cs
  47. 13 0
      Application/Features/Member/SummaryChangeLog/Search/Query.cs
  48. 20 0
      Application/Features/Member/SummaryChangeLog/Search/Response.cs
  49. 16 7
      Infrastructure/Persistence/AppDbContext.cs
  50. 0 1
      Infrastructure/Persistence/Configurations/Members/Logs/MemberLoginLogConfiguration.cs
  51. 2276 0
      Infrastructure/Persistence/Migrations/20260205091731_PendingChanges.Designer.cs
  52. 22 0
      Infrastructure/Persistence/Migrations/20260205091731_PendingChanges.cs
  53. 2276 0
      Infrastructure/Persistence/Migrations/20260205103258_AddMemberLogs.Designer.cs
  54. 22 0
      Infrastructure/Persistence/Migrations/20260205103258_AddMemberLogs.cs

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

@@ -2,7 +2,12 @@
   "permissions": {
   "permissions": {
     "allow": [
     "allow": [
       "Bash(dotnet build:*)",
       "Bash(dotnet build:*)",
-      "Bash(dotnet ef migrations add:*)"
+      "Bash(dotnet ef migrations add:*)",
+      "mcp__Desktop_Commander__start_process"
     ]
     ]
-  }
-}
+  },
+  "additionalDirectories": [
+	"E:\\workspace\\bitforum.io"
+  ],
+  "defaultMode": "acceptEdits"
+}

+ 0 - 0
Admin/Application/Features/Banner/Item/Delete/Command.cs


+ 0 - 0
Admin/Application/Features/Banner/Item/Delete/Handler.cs


+ 0 - 0
Admin/Application/Features/Banner/Item/Update/Handler.cs


+ 0 - 0
Admin/Application/Features/Banner/Position/Save/Handler.cs


+ 150 - 0
Admin/Pages/Member/Log/Email.cshtml

@@ -0,0 +1,150 @@
+@page
+@model Admin.Pages.Member.Log.EmailModel
+@{
+    ViewData["Title"] = "이메일 변경 내역";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form id="fAdminSearch" method="get" accept-charset="utf-8" autocomplete="off">
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+
+        <div class="row g-2 mb-2">
+            <div class="col-12 col-lg-auto">
+                <div class="row g-2">
+                    <div class="col-auto col-md-auto">
+                        <select name="search" class="form-select">
+                            <option value="1" selected="@(Model.Query.Search == 1)">회원ID</option>
+                            <option value="2" selected="@(Model.Query.Search == 2)">회원 이메일</option>
+                            <option value="3" selected="@(Model.Query.Search == 3)">회원 별명</option>
+                            <option value="4" selected="@(Model.Query.Search == 4)">이전 이메일</option>
+                            <option value="5" selected="@(Model.Query.Search == 5)">변경 이메일</option>
+                        </select>
+                    </div>
+                    <div class="col col-md-auto">
+                        <input type="search" name="keyword" class="form-control" maxlength="100" value="@Model.Query.Keyword" />
+                    </div>
+                </div>
+            </div>
+            <div class="col-12 col-sm">
+                <div class="row g-2">
+                    <div class="col-12 col-md-auto">
+                        <div class="row row-cols-2 g-2">
+                            <div class="col">
+                                <input type="datetime-local" name="startAt" class="form-control" value="@Model.Query.StartAt" />
+                            </div>
+                            <div class="col d-none">
+                                ~
+                            </div>
+                            <div class="col">
+                                <input type="datetime-local" name="endAt" class="form-control" value="@Model.Query.EndAt" />
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col col-md-auto text-center">
+                        <button type="submit" class="btn btn-primary w-100">검색</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form>
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <select name="perPage" id="perPage" class="form-select w-auto d-inline-block" form="fAdminSearch">
+                <option value="10" selected="@(Model.Query.PerPage == 10)">10</option>
+                <option value="20" selected="@(Model.Query.PerPage == 20)">20</option>
+                <option value="50" selected="@(Model.Query.PerPage == 50)">50</option>
+                <option value="100" selected="@(Model.Query.PerPage == 100)">100</option>
+            </select>
+            <button type="submit" id="btnListDelete" class="btn btn-danger" form="fAdminList" disabled>삭제</button>
+        </div>
+    </div>
+
+    <form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+        @Html.AntiForgeryToken()
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+        <input type="hidden" name="perPage" value="@Model.Query.PerPage" />
+        <input type="hidden" name="keyword" value="@Model.Query.Keyword" />
+        <input type="hidden" name="startAt" value="@Model.Query.StartAt" />
+        <input type="hidden" name="endAt" value="@Model.Query.EndAt" />
+    </form>
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered table-hover mt-3">
+            <colgroup>
+                <col style="width: 5%;" />
+                <col style="width: 20%;" />
+                <col style="width: 20%;" />
+                <col style="width: 27%;" />
+                <col style="width: 15%;" />
+                <col style="width: 10%;" />
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>
+                        <div class="form-check-inline">
+                            <input type="checkbox" id="checkedAll" class="form-check-input" value="1" form="fAdminList" />
+                            <label for="checkedAll">ID</label>
+                        </div>
+                    </th>
+                    <th>이전 이메일</th>
+                    <th>변경 이메일</th>
+                    <th>계정</th>
+                    <th>등록일시</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Data == null || Model.Data.Count <= 0)
+                {
+                    <tr>
+                        <td colspan="6">No Data.</td>
+                    </tr>
+                }
+                else
+                {
+                    @foreach (var row in Model.Data)
+                    {
+                        <tr>
+                            <td>
+                                <div class="form-check-inline">
+                                    <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" />
+                                    <label for="ids_@row.ID">@row.ID</label>
+                                </div>
+                            </td>
+                            <td>@row.ID</td>
+                            <td>@row.BeforeEmail</td>
+                            <td>@row.AfterEmail</td>
+                            <td>[@row.MemberID] @row.MemberEmail</td>
+                            <td>@row.CreatedAt</td>
+                            <td>
+                                <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                            </td>
+                        </tr>
+                    }
+                }
+            </tbody>
+        </table>
+
+        <partial name="_Pagination" model="Model.Pagination" />
+    </div>
+</div>
+
+@section Scripts {
+    <script>
+        let searchForm = document.getElementById("fAdminSearch");
+
+        $(document).on("change", "#perPage", function () {
+            searchForm.elements["pageNum"].value = 1;
+            searchForm.submit();
+        });
+    </script>
+}

+ 108 - 0
Admin/Pages/Member/Log/Email.cshtml.cs

@@ -0,0 +1,108 @@
+using SharedKernel.Helpers;
+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.Log;
+
+public class EmailModel(IMediator mediator) : PageModel
+{
+    [BindProperty(SupportsGet = true)]
+    public QueryParams Query { get; set; } = new();
+
+    public sealed class QueryParams
+    {
+        [Range(1, int.MaxValue)]
+        [DisplayName("페이지 번호")]
+        public int PageNum { get; set; } = 1;
+
+        [Range(1, 100)]
+        [DisplayName("페이지 목록 수")]
+        public ushort PerPage { get; set; } = 10;
+
+        [DisplayName("검색 조건")]
+        [Range(1, 2, ErrorMessage = "{0}이(가) 올바르지 않습니다.")]
+        public int? Search { get; set; }
+
+        [DisplayName("검색어")]
+        [MaxLength(255, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+        public string? Keyword { get; set; }
+
+        [DisplayName("시작일")]
+        public string? StartAt { get; set; }
+
+        [DisplayName("종료일")]
+        public string? EndAt { get; set; }
+    }
+
+    public int Total { get; set; } = 0;
+
+    public List<(
+        int Num,
+        int ID,
+        int MemberID,
+        string MemberEmail,
+        string? BeforeEmail,
+        string? AfterEmail,
+        string IpAddress,
+        string CreatedAt
+    )> Data { get; set; } = [];
+
+    public Pagination? Pagination { get; set; }
+
+    public async Task OnGetAsync(CancellationToken ct)
+    {
+        if (!ModelState.IsValid)
+        {
+            return;
+        }
+
+        var result = await mediator.Send(new SearchEmailChangeLogs.Query(
+            Query.PageNum,
+            Query.PerPage,
+            Query.Search,
+            Query.Keyword,
+            Query.StartAt,
+            Query.EndAt
+        ), ct);
+
+        Total = result.Total;
+        Data = [..result.List.Select(c =>
+        (
+            c.Num,
+            c.ID,
+            c.MemberID,
+            c.MemberEmail,
+            c.BeforeEmail ?? "-",
+            c.AfterEmail ?? "-",
+            c.IpAddress ?? "-",
+            c.CreatedAt.GetDateAt()
+        ))];
+
+        Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
+    }
+
+    public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+    {
+        try
+        {
+            if (ids.Length == 0)
+            {
+                throw new Exception("삭제할 항목을 선택해주세요.");
+            }
+
+            await mediator.Send(new DeleteEmailChangeLog.Command(ids), ct);
+
+            TempData["SuccessMessage"] = $"{ids.Length}건이 삭제되었습니다.";
+        }
+        catch (Exception e)
+        {
+            TempData["ErrorMessage"] = e.Message;
+        }
+
+        return RedirectToPage(Query);
+    }
+}

+ 203 - 0
Admin/Pages/Member/Log/Intro.cshtml

@@ -0,0 +1,203 @@
+@page
+@model Admin.Pages.Member.Log.IntroModel
+@{
+    ViewData["Title"] = "자기소개 변경 내역";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form id="fAdminSearch" method="get" accept-charset="utf-8" autocomplete="off">
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+
+        <div class="row g-2 mb-2">
+            <div class="col-12 col-lg-auto">
+                <div class="row g-2">
+                    <div class="col-auto col-md-auto">
+                        <select name="search" class="form-select">
+                            <option value="1" selected="@(Model.Query.Search == 1)">회원ID</option>
+                            <option value="2" selected="@(Model.Query.Search == 2)">회원 이메일</option>
+                            <option value="3" selected="@(Model.Query.Search == 3)">회원 별명</option>
+                            <option value="4" selected="@(Model.Query.Search == 4)">이전 자기소개</option>
+                            <option value="5" selected="@(Model.Query.Search == 5)">변경 자기소개</option>
+                        </select>
+                    </div>
+                    <div class="col col-md-auto">
+                        <input type="search" name="keyword" class="form-control" maxlength="100" value="@Model.Query.Keyword" />
+                    </div>
+                </div>
+            </div>
+            <div class="col-12 col-sm">
+                <div class="row g-2">
+                    <div class="col-12 col-md-auto">
+                        <div class="row row-cols-2 g-2">
+                            <div class="col">
+                                <input type="datetime-local" name="startAt" class="form-control" value="@Model.Query.StartAt" />
+                            </div>
+                            <div class="col d-none">
+                                ~
+                            </div>
+                            <div class="col">
+                                <input type="datetime-local" name="endAt" class="form-control" value="@Model.Query.EndAt" />
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col col-md-auto text-center">
+                        <button type="submit" class="btn btn-primary w-100">검색</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form>
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <select name="perPage" id="perPage" class="form-select w-auto d-inline-block" form="fAdminSearch">
+                <option value="10" selected="@(Model.Query.PerPage == 10)">10</option>
+                <option value="20" selected="@(Model.Query.PerPage == 20)">20</option>
+                <option value="50" selected="@(Model.Query.PerPage == 50)">50</option>
+                <option value="100" selected="@(Model.Query.PerPage == 100)">100</option>
+            </select>
+            <button type="submit" id="btnListDelete" class="btn btn-danger" form="fAdminList" disabled>삭제</button>
+        </div>
+    </div>
+
+    <form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+        @Html.AntiForgeryToken()
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+        <input type="hidden" name="perPage" value="@Model.Query.PerPage" />
+        <input type="hidden" name="keyword" value="@Model.Query.Keyword" />
+        <input type="hidden" name="startAt" value="@Model.Query.StartAt" />
+        <input type="hidden" name="endAt" value="@Model.Query.EndAt" />
+    </form>
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered table-hover mt-3">
+            <colgroup>
+                <col style="width: 5%;" />
+                <col style="width: 23%;" />
+                <col style="width: 23%;" />
+                <col style="width: 23%;" />
+                <col style="width: 15%;" />
+                <col style="width: 8%;" />
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>
+                        <div class="form-check-inline">
+                            <input type="checkbox" id="checkedAll" class="form-check-input" value="1" form="fAdminList" />
+                            <label for="checkedAll">ID</label>
+                        </div>
+                    </th>
+                    <th>이전 자기소개</th>
+                    <th>변경 자기소개</th>
+                    <th>계정</th>
+                    <th>등록일시</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Data == null || Model.Data.Count <= 0)
+                {
+                    <tr>
+                        <td colspan="6">No Data.</td>
+                    </tr>
+                }
+                else
+                {
+                    @foreach (var row in Model.Data)
+                    {
+                        <tr>
+                            <td>
+                                <div class="form-check-inline">
+                                    <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" />
+                                    <label for="ids_@row.ID">@row.ID</label>
+                                </div>
+                            </td>
+                            <td>
+                                @if (!string.IsNullOrEmpty(row.BeforeIntro))
+                                {
+                                    <button type="button" class="btn btn-sm btn-link" data-bs-toggle="modal" data-bs-target="#beforeIntro_@(row.ID)">
+                                        상세보기
+                                    </button>
+                                    <div class="modal fade" id="beforeIntro_@(row.ID)" tabindex="-1" aria-labelledby="beforeIntro_@(row.ID)_Label" aria-hidden="true">
+                                        <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
+                                            <div class="modal-content">
+                                                <div class="modal-header bg-dark text-white">
+                                                    <h5 class="modal-title">이전 자기소개</h5>
+                                                    <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
+                                                </div>
+                                                <div class="modal-body">
+                                                    @Html.Raw(row.BeforeIntro)
+                                                </div>
+                                                <div class="modal-footer justify-content-center">
+                                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">확인</button>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                }
+                                else
+                                {
+                                    <text>-</text>
+                                }
+                            </td>
+                            <td>
+                                @if (!string.IsNullOrEmpty(row.AfterIntro))
+                                {
+                                    <button type="button" class="btn btn-sm btn-link" data-bs-toggle="modal" data-bs-target="#afterIntro_@(row.ID)">
+                                        상세보기
+                                    </button>
+                                    <div class="modal fade" id="afterIntro_@(row.ID)" tabindex="-1" aria-labelledby="afterIntro_@(row.ID)_Label" aria-hidden="true">
+                                        <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
+                                            <div class="modal-content">
+                                                <div class="modal-header bg-dark text-white">
+                                                    <h5 class="modal-title">변경 자기소개</h5>
+                                                    <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
+                                                </div>
+                                                <div class="modal-body">
+                                                    @Html.Raw(row.AfterIntro)
+                                                </div>
+                                                <div class="modal-footer justify-content-center">
+                                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">확인</button>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                }
+                                else
+                                {
+                                    <text>-</text>
+                                }
+                            </td>
+                            <td>[@row.MemberID] @row.MemberEmail</td>
+                            <td>@row.CreatedAt</td>
+                            <td>
+                                <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                            </td>
+                        </tr>
+                    }
+                }
+            </tbody>
+        </table>
+
+        <partial name="_Pagination" model="Model.Pagination" />
+    </div>
+</div>
+
+@section Scripts {
+    <script>
+        let searchForm = document.getElementById("fAdminSearch");
+
+        $(document).on("change", "#perPage", function () {
+            searchForm.elements["pageNum"].value = 1;
+            searchForm.submit();
+        });
+    </script>
+}

+ 104 - 0
Admin/Pages/Member/Log/Intro.cshtml.cs

@@ -0,0 +1,104 @@
+using SharedKernel.Helpers;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using SharedKernel.Extensions;
+
+namespace Admin.Pages.Member.Log;
+
+public class IntroModel(IMediator mediator) : PageModel
+{
+    [BindProperty(SupportsGet = true)]
+    public QueryParams Query { get; set; } = new();
+
+    public sealed class QueryParams
+    {
+        [Range(1, int.MaxValue)]
+        [DisplayName("페이지 번호")]
+        public int PageNum { get; set; } = 1;
+
+        [Range(1, 100)]
+        [DisplayName("페이지 목록 수")]
+        public ushort PerPage { get; set; } = 10;
+
+        [DisplayName("검색 조건")]
+        [Range(1, 2, ErrorMessage = "{0}이(가) 올바르지 않습니다.")]
+        public int? Search { get; set; }
+
+        [DisplayName("검색어")]
+        [MaxLength(255, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+        public string? Keyword { get; set; }
+
+        [DisplayName("시작일")]
+        public string? StartAt { get; set; }
+
+        [DisplayName("종료일")]
+        public string? EndAt { get; set; }
+    }
+
+    public int Total { get; set; } = 0;
+
+    public List<(
+        int ID,
+        int MemberID,
+        string MemberEmail,
+        string? BeforeIntro,
+        string? AfterIntro,
+        string CreatedAt
+    )> Data { get; set; } = [];
+
+    public Pagination? Pagination { get; set; }
+
+    public async Task OnGetAsync(CancellationToken ct)
+    {
+        if (!ModelState.IsValid)
+        {
+            return;
+        }
+
+        var result = await mediator.Send(new SearchIntroChangeLogs.Query(
+            Query.PageNum,
+            Query.PerPage,
+            Query.Search,
+            Query.Keyword,
+            Query.StartAt,
+            Query.EndAt
+        ), ct);
+
+        Total = result.Total;
+        Data = [..result.List.Select(c =>
+        (
+            c.ID,
+            c.MemberID,
+            c.MemberEmail,
+            c.BeforeIntro ?? "-",
+            c.AfterIntro ?? "-",
+            c.CreatedAt.GetDateAt()
+        ))];
+
+        Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
+    }
+
+    public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+    {
+        try
+        {
+            if (ids.Length == 0)
+            {
+                throw new Exception("삭제할 항목을 선택해주세요.");
+            }
+
+            await mediator.Send(new DeleteIntroChangeLog.Command(ids), ct);
+
+            TempData["SuccessMessage"] = $"{ids.Length}건이 삭제되었습니다.";
+        }
+        catch (Exception e)
+        {
+            TempData["ErrorMessages"] = e.Message;
+        }
+
+        return RedirectToPage(Query);
+    }
+}

+ 153 - 0
Admin/Pages/Member/Log/Login/Index.cshtml

@@ -0,0 +1,153 @@
+@page
+@model Admin.Pages.Member.Log.Login.IndexModel
+@{
+    ViewData["Title"] = "로그인 내역";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form id="fAdminSearch" method="get" accept-charset="utf-8" autocomplete="off">
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+
+        <div class="row g-2 mb-2">
+            <div class="col-12 col-lg-auto">
+                <div class="row g-2">
+                    <div class="col-auto col-md-auto">
+                        <select name="search" class="form-select">
+                            <option value="1" selected="@(Model.Query.Search == 1)">회원ID</option>
+                            <option value="2" selected="@(Model.Query.Search == 2)">회원 이메일</option>
+                            <option value="3" selected="@(Model.Query.Search == 3)">회원 별명</option>
+                        </select>
+                    </div>
+                    <div class="col col-md-auto">
+                        <input type="search" name="keyword" class="form-control" maxlength="100" value="@Model.Query.Keyword" />
+                    </div>
+                </div>
+            </div>
+            <div class="col-12 col-sm">
+                <div class="row g-2">
+                    <div class="col-12 col-md-auto">
+                        <div class="row row-cols-2 g-2">
+                            <div class="col">
+                                <input type="datetime-local" name="startAt" class="form-control" value="@Model.Query.StartAt" />
+                            </div>
+                            <div class="col d-none">
+                                ~
+                            </div>
+                            <div class="col">
+                                <input type="datetime-local" name="endAt" class="form-control" value="@Model.Query.EndAt" />
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col col-md-auto text-center">
+                        <button type="submit" class="btn btn-primary w-100">검색</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form>
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <select name="perPage" id="perPage" class="form-select w-auto d-inline-block" form="fAdminSearch">
+                <option value="10" selected="@(Model.Query.PerPage == 10)">10</option>
+                <option value="20" selected="@(Model.Query.PerPage == 20)">20</option>
+                <option value="50" selected="@(Model.Query.PerPage == 50)">50</option>
+                <option value="100" selected="@(Model.Query.PerPage == 100)">100</option>
+            </select>
+            <button type="button" id="btnListDelete" class="btn btn-danger" form="fAdminList" disabled>삭제</button>
+        </div>
+    </div>
+
+    <form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+        @Html.AntiForgeryToken()
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+        <input type="hidden" name="perPage" value="@Model.Query.PerPage" />
+        <input type="hidden" name="keyword" value="@Model.Query.Keyword" />
+        <input type="hidden" name="startAt" value="@Model.Query.StartAt" />
+        <input type="hidden" name="endAt" value="@Model.Query.EndAt" />
+    </form>
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered table-hover mt-3">
+            <thead>
+                <tr>
+                    <th>
+                        <div class="form-check-inline">
+                            <input type="checkbox" id="checkedAll" class="form-check-input" value="1" form="fAdminList" />
+                            <label for="checkedAll">ID</label>
+                        </div>
+                    </th>
+                    <th>성공</th>
+                    <th>계정</th>
+                    <th>사유</th>
+                    <th>IP</th>
+                    <th>브라우저</th>
+                    <th>장치</th>
+                    <th>OS</th>
+                    <th>등록일시</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.List == null || Model.List.Count <= 0)
+                {
+                    <tr>
+                        <td colspan="11">No Data.</td>
+                    </tr>
+                }
+                else
+                {
+                    @foreach (var row in Model.List)
+                    {
+                        <tr>
+                            <td>
+                                <div class="form-check-inline">
+                                    <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" />
+                                    <label for="ids_@row.ID">@row.ID</label>
+                                </div>
+                            </td>
+                            <td>@row.ID</td>
+                            <td>@row.Success</td>
+                            <td>
+                                @row.Account @if(row.MemberName is not null) { <small>, @row.MemberName</small> }
+                            </td>
+                            <td>@row.Reason</td>
+                            <td>@row.IpAddress</td>
+                            <td>@row.Browser</td>
+                            <td>@row.Device</td>
+                            <td>@row.OS</td>
+                            <td>@row.CreatedAt</td>
+                            <td>
+                                <div class="d-grid gap-2 d-block d-xxl-inline">
+                                    <a class="btn btn-sm btn-outline-info" href="@row.ViewURL">상세</a>
+                                    <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                                </div>
+                            </td>
+                        </tr>
+                    }
+                }
+            </tbody>
+        </table>
+
+        <partial name="_Pagination" model="Model.Pagination" />
+    </div>
+</div>
+
+@section Scripts {
+    <script>
+        let searchForm = document.getElementById("fAdminSearch");
+
+        $(document).on("change", "#perPage", function () {
+            searchForm.elements["pageNum"].value = 1;
+            searchForm.submit();
+        });
+    </script>
+}

+ 119 - 0
Admin/Pages/Member/Log/Login/Index.cshtml.cs

@@ -0,0 +1,119 @@
+using SharedKernel.Helpers;
+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.Log.Login;
+
+public class IndexModel(IMediator mediator) : PageModel
+{
+    [BindProperty(SupportsGet = true)]
+    public QueryParams Query { get; set; } = new();
+
+    public sealed class QueryParams
+    {
+        [Range(1, int.MaxValue)]
+        [DisplayName("페이지 번호")]
+        public int PageNum { get; set; } = 1;
+
+        [Range(1, 100)]
+        [DisplayName("페이지 목록 수")]
+        public ushort PerPage { get; set; } = 10;
+
+        [DisplayName("검색 조건")]
+        [Range(1, 3, ErrorMessage = "{0}이(가) 올바르지 않습니다.")]
+        public int? Search { get; set; }
+
+        [DisplayName("검색어")]
+        [MaxLength(255, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+        public string? Keyword { get; set; }
+
+        [DisplayName("시작일")]
+        public string? StartAt { get; set; }
+
+        [DisplayName("종료일")]
+        public string? EndAt { get; set; }
+    }
+
+    public int Total { get; set; } = 0;
+
+    public List<(
+        int Num,
+        int ID,
+        int? MemberID,
+        string? MemberName,
+        string Account,
+        bool Success,
+        string? Reason,
+        string? IpAddress,
+        string? UserAgent,
+        string? Browser,
+        string? OS,
+        string? Device,
+        string CreatedAt,
+        string? ViewURL
+    )> List { get; set; } = [];
+
+    public Pagination? Pagination { get; set; }
+
+    public async Task OnGetAsync(CancellationToken ct)
+    {
+        if (!ModelState.IsValid)
+        {
+            return;
+        }
+
+        var result = await mediator.Send(new SearchLoginLogs.Query(
+            Query.PageNum,
+            Query.PerPage,
+            Query.Search,
+            Query.Keyword,
+            Query.StartAt,
+            Query.EndAt
+        ), ct);
+
+        Total = result.Total;
+        List = [..result.List.Select(c => (
+            c.Num,
+            c.ID,
+            c.MemberID,
+            c.MemberName ?? "-",
+            c.Account,
+            c.Success,
+            c.Reason,
+            c.IpAddress,
+            c.UserAgent ?? "-",
+            c.Browser ?? "-",
+            c.OS ?? "-",
+            c.Device ?? "-",
+            c.CreatedAt.GetDateAt(),
+            $"/Member/Log/Login/Info/{c.ID}${Request.QueryString}"
+        ))];
+
+        Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
+    }
+
+    public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+    {
+        try
+        {
+            if (ids.Length == 0)
+            {
+                throw new Exception("삭제할 항목을 선택해주세요.");
+            }
+
+            await mediator.Send(new DeleteLoginLog.Command(ids), ct);
+
+            TempData["SuccessMessage"] = $"{ids.Length}건이 삭제되었습니다.";
+        }
+        catch (Exception e)
+        {
+            TempData["ErrorMessages"] = e.Message;
+        }
+
+        return RedirectToPage(Query);
+    }
+}

+ 91 - 0
Admin/Pages/Member/Log/Login/Info.cshtml

@@ -0,0 +1,91 @@
+@page "{id:int}"
+@model Admin.Pages.Member.Log.Login.InfoModel
+@{
+    ViewData["Title"] = "로그인 상세 정보";
+}
+
+<div class="container">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    @if (Model.Data == null)
+    {
+        <div class="alert alert-warning">데이터를 찾을 수 없습니다.</div>
+    }
+    else
+    {
+        <div class="row mb-3">
+            <label class="col-sm-2">PK</label>
+            <div class="col-sm-10">@Model.Data.ID</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">회원</label>
+            <div class="col-sm-10">
+                @if (Model.Data.MemberID != null)
+                {
+                    <span>@Model.Data.MemberID</span>
+                }
+                else
+                {
+                    <text>비회원</text>
+                }
+                @if (Model.Data.MemberName is not null)
+                {
+                    <small>, @Model.Data.MemberName</small>
+                }
+            </div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">시도 계정</label>
+            <div class="col-sm-10">@Model.Data.Account</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">성공 여부</label>
+            <div class="col-sm-10">@(Model.Data.Success ? "성공" : "실패")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">사유</label>
+            <div class="col-sm-10">@(Model.Data.Reason ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">Referer</label>
+            <div class="col-sm-10">@(!string.IsNullOrEmpty(Model.Data.Referer) ? Model.Data.Referer : "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">URL</label>
+            <div class="col-sm-10">@(Model.Data.Url ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">IP</label>
+            <div class="col-sm-10">@(Model.Data.IpAddress ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">User Agent</label>
+            <div class="col-sm-10">@(Model.Data.UserAgent ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">등록 일시</label>
+            <div class="col-sm-10">@Model.Data.CreatedAt</div>
+        </div>
+        <hr />
+        <div class="row mb-3">
+            <label class="col-sm-2">Browser</label>
+            <div class="col-sm-10">@(Model.Data.Browser ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">OS</label>
+            <div class="col-sm-10">@(Model.Data.OS ?? "-")</div>
+        </div>
+        <div class="row mb-3">
+            <label class="col-sm-2">Device</label>
+            <div class="col-sm-10">@(Model.Data.Device ?? "-")</div>
+        </div>
+    }
+    <hr />
+    <div class="d-grid gap-2 text-center d-md-block">
+        <a asp-page="Index" class="btn btn-sm btn-secondary">목록</a>
+    </div>
+    <br />
+</div>

+ 18 - 0
Admin/Pages/Member/Log/Login/Info.cshtml.cs

@@ -0,0 +1,18 @@
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using GetLoginLog = Application.Features.Member.LoginLog.Get;
+
+namespace Admin.Pages.Member.Log.Login;
+
+public class InfoModel(IMediator mediator) : PageModel
+{
+    public GetLoginLog.Response? Data { get; set; }
+
+    public async Task<IActionResult> OnGetAsync(int id, CancellationToken ct)
+    {
+        Data = await mediator.Send(new GetLoginLog.Query(id), ct);
+
+        return Page();
+    }
+}

+ 150 - 0
Admin/Pages/Member/Log/Name.cshtml

@@ -0,0 +1,150 @@
+@page
+@model Admin.Pages.Member.Log.NameModel
+@{
+    ViewData["Title"] = "별명 변경 내역";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form id="fAdminSearch" method="get" accept-charset="utf-8" autocomplete="off">
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+
+        <div class="row g-2 mb-2">
+            <div class="col-12 col-lg-auto">
+                <div class="row g-2">
+                    <div class="col-auto col-md-auto">
+                        <select name="search" class="form-select">
+                            <option value="1" selected="@(Model.Query.Search == 1)">회원ID</option>
+                            <option value="2" selected="@(Model.Query.Search == 2)">회원 이메일</option>
+                            <option value="3" selected="@(Model.Query.Search == 3)">회원 별명</option>
+                            <option value="4" selected="@(Model.Query.Search == 4)">이전 별명</option>
+                            <option value="5" selected="@(Model.Query.Search == 5)">변경 별명</option>
+                        </select>
+                    </div>
+                    <div class="col col-md-auto">
+                        <input type="search" name="keyword" class="form-control" maxlength="100" value="@Model.Query.Keyword" />
+                    </div>
+                </div>
+            </div>
+            <div class="col-12 col-sm">
+                <div class="row g-2">
+                    <div class="col-12 col-md-auto">
+                        <div class="row row-cols-2 g-2">
+                            <div class="col">
+                                <input type="datetime-local" name="startAt" class="form-control" value="@Model.Query.StartAt" />
+                            </div>
+                            <div class="col d-none">
+                                ~
+                            </div>
+                            <div class="col">
+                                <input type="datetime-local" name="endAt" class="form-control" value="@Model.Query.EndAt" />
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col col-md-auto text-center">
+                        <button type="submit" class="btn btn-primary w-100">검색</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form>
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <select name="perPage" id="perPage" class="form-select w-auto d-inline-block" form="fAdminSearch">
+                <option value="10" selected="@(Model.Query.PerPage == 10)">10</option>
+                <option value="20" selected="@(Model.Query.PerPage == 20)">20</option>
+                <option value="50" selected="@(Model.Query.PerPage == 50)">50</option>
+                <option value="100" selected="@(Model.Query.PerPage == 100)">100</option>
+            </select>
+            <button type="submit" id="btnListDelete" class="btn btn-danger" form="fAdminList" disabled>삭제</button>
+        </div>
+    </div>
+
+    <form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+        @Html.AntiForgeryToken()
+        <input type="hidden" name="pageNum" value="@Model.Query.PageNum" />
+        <input type="hidden" name="perPage" value="@Model.Query.PerPage" />
+        <input type="hidden" name="keyword" value="@Model.Query.Keyword" />
+        <input type="hidden" name="startAt" value="@Model.Query.StartAt" />
+        <input type="hidden" name="endAt" value="@Model.Query.EndAt" />
+    </form>
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered table-hover mt-3">
+            <colgroup>
+                <col style="width: 5%;" />
+                <col style="width: 20%;" />
+                <col style="width: 20%;" />
+                <col style="width: 27%;" />
+                <col style="width: 15%;" />
+                <col style="width: 10%;" />
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>
+                        <div class="form-check-inline">
+                            <input type="checkbox" id="checkedAll" class="form-check-input" value="1" form="fAdminList" />
+                            <label for="checkedAll">ID</label>
+                        </div>
+                    </th>
+                    <th>이전 별명</th>
+                    <th>변경 별명</th>
+                    <th>계정</th>
+                    <th>등록일시</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Data == null || Model.Data.Count <= 0)
+                {
+                    <tr>
+                        <td colspan="6">No Data.</td>
+                    </tr>
+                }
+                else
+                {
+                    @foreach (var row in Model.Data)
+                    {
+                        <tr>
+                            <td>
+                                <div class="form-check-inline">
+                                    <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" />
+                                    <label for="ids_@row.ID">@row.ID</label>
+                                </div>
+                            </td>
+                            <td>@row.ID</td>
+                            <td>@row.BeforeName</td>
+                            <td>@row.AfterName</td>
+                            <td>[@row.MemberID] @row.MemberEmail</td>
+                            <td>@row.CreatedAt</td>
+                            <td>
+                                <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                            </td>
+                        </tr>
+                    }
+                }
+            </tbody>
+        </table>
+
+        <partial name="_Pagination" model="Model.Pagination" />
+    </div>
+</div>
+
+@section Scripts {
+    <script>
+        let searchForm = document.getElementById("fAdminSearch");
+
+        $(document).on("change", "#perPage", function () {
+            searchForm.elements["pageNum"].value = 1;
+            searchForm.submit();
+        });
+    </script>
+}

+ 108 - 0
Admin/Pages/Member/Log/Name.cshtml.cs

@@ -0,0 +1,108 @@
+using SharedKernel.Helpers;
+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.Log;
+
+public class NameModel(IMediator mediator) : PageModel
+{
+    [BindProperty(SupportsGet = true)]
+    public QueryParams Query { get; set; } = new();
+
+    public sealed class QueryParams
+    {
+        [Range(1, int.MaxValue)]
+        [DisplayName("페이지 번호")]
+        public int PageNum { get; set; } = 1;
+
+        [Range(1, 100)]
+        [DisplayName("페이지 목록 수")]
+        public ushort PerPage { get; set; } = 10;
+
+        [DisplayName("검색 조건")]
+        [Range(1, 5, ErrorMessage = "{0}이(가) 올바르지 않습니다.")]
+        public int? Search { get; set; }
+
+        [DisplayName("검색어")]
+        [MaxLength(255, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+        public string? Keyword { get; set; }
+
+        [DisplayName("시작일")]
+        public string? StartAt { get; set; }
+
+        [DisplayName("종료일")]
+        public string? EndAt { get; set; }
+    }
+
+    public int Total { get; set; } = 0;
+
+    public List<(
+        int Num,
+        int ID,
+        int MemberID,
+        string MemberEmail,
+        string? BeforeName,
+        string? AfterName,
+        string IpAddress,
+        string CreatedAt
+    )> Data { get; set; } = [];
+
+    public Pagination? Pagination { get; set; }
+
+    public async Task OnGetAsync(CancellationToken ct)
+    {
+        if (!ModelState.IsValid)
+        {
+            return;
+        }
+
+        var result = await mediator.Send(new SearchNameChangeLogs.Query(
+            Query.PageNum,
+            Query.PerPage,
+            Query.Search,
+            Query.Keyword,
+            Query.StartAt,
+            Query.EndAt
+        ), ct);
+
+        Total = result.Total;
+        Data = [..result.List.Select(c =>
+        (
+            c.Num,
+            c.ID,
+            c.MemberID,
+            c.MemberEmail,
+            c.BeforeName ?? "-",
+            c.AfterName ?? "-",
+            c.IpAddress ?? "-",
+            c.CreatedAt.GetDateAt()
+        ))];
+
+        Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
+    }
+
+    public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+    {
+        try
+        {
+            if (ids.Length == 0)
+            {
+                throw new Exception("삭제할 항목을 선택해주세요.");
+            }
+
+            await mediator.Send(new DeleteNameChangeLog.Command(ids), ct);
+
+            TempData["SuccessMessage"] = $"{ids.Length}건이 삭제되었습니다.";
+        }
+        catch (Exception e)
+        {
+            TempData["ErrorMessage"] = e.Message;
+        }
+
+        return RedirectToPage(Query);
+    }
+}

+ 144 - 0
Admin/Pages/Member/Log/Summary.cshtml

@@ -0,0 +1,144 @@
+@page
+@model Admin.Pages.Member.Log.SummaryModel
+@{
+    ViewData["Title"] = "한마디 변경 내역";
+}
+
+<div class="container-fluid">
+    <h3>@ViewData["Title"]</h3>
+    <hr />
+
+    <partial name="_StatusMessage" />
+
+    <form id="fAdminSearch" method="get" accept-charset="utf-8" autocomplete="off">
+        <input type="hidden" name="pageNum" value="1" />
+
+        <div class="row g-2 mb-2">
+            <div class="col-12 col-lg-auto">
+                <div class="row g-2">
+                    <div class="col-auto col-md-auto">
+                        <select name="search" class="form-select">
+                            <option value="1" selected="@(Model.Query.Search == 1)">회원ID</option>
+                            <option value="2" selected="@(Model.Query.Search == 2)">회원 이메일</option>
+                            <option value="3" selected="@(Model.Query.Search == 3)">회원 별명</option>
+                            <option value="4" selected="@(Model.Query.Search == 4)">이전 한마디</option>
+                            <option value="5" selected="@(Model.Query.Search == 5)">변경 한마디</option>
+                        </select>
+                    </div>
+                    <div class="col col-md-auto">
+                        <input type="search" name="keyword" class="form-control" maxlength="100" value="@Model.Query.Keyword" />
+                    </div>
+                </div>
+            </div>
+            <div class="col-12 col-sm">
+                <div class="row g-2">
+                    <div class="col-12 col-md-auto">
+                        <div class="row row-cols-2 g-2">
+                            <div class="col">
+                                <input type="datetime-local" name="startAt" class="form-control" value="@Model.Query.StartAt" />
+                            </div>
+                            <div class="col d-none">
+                                ~
+                            </div>
+                            <div class="col">
+                                <input type="datetime-local" name="endAt" class="form-control" value="@Model.Query.EndAt" />
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col col-md-auto text-center">
+                        <button type="submit" class="btn btn-primary w-100">검색</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form>
+
+    <div class="row g-2 align-items-end">
+        <div class="col">
+            Total : @Model.Total
+        </div>
+        <div class="col text-end">
+            <select name="perPage" id="perPage" class="form-select w-auto d-inline-block" form="fAdminSearch">
+                <option value="10" selected="@(Model.Query.PerPage == 10)">10</option>
+                <option value="20" selected="@(Model.Query.PerPage == 20)">20</option>
+                <option value="50" selected="@(Model.Query.PerPage == 50)">50</option>
+                <option value="100" selected="@(Model.Query.PerPage == 100)">100</option>
+            </select>
+            <button type="submit" id="btnListDelete" class="btn btn-danger" form="fAdminList" disabled>삭제</button>
+        </div>
+    </div>
+
+    <form id="fAdminList" method="post" accept-charset="utf-8" asp-page-handler="Delete">
+        @Html.AntiForgeryToken()
+    </form>
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered table-hover mt-3">
+            <colgroup>
+                <col style="width: 5%;" />
+                <col style="width: 25%;" />
+                <col style="width: 25%;" />
+                <col style="width: 22%;" />
+                <col style="width: 12%;" />
+                <col style="width: 8%;" />
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>
+                        <div class="form-check-inline">
+                            <input type="checkbox" id="checkedAll" class="form-check-input" value="1" form="fAdminList" />
+                            <label for="checkedAll">ID</label>
+                        </div>
+                    </th>
+                    <th>이전 한마디</th>
+                    <th>변경 한마디</th>
+                    <th>계정</th>
+                    <th>등록일시</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Data == null || Model.Data.Count <= 0)
+                {
+                    <tr>
+                        <td colspan="6">No Data.</td>
+                    </tr>
+                }
+                else
+                {
+                    @foreach (var row in Model.Data)
+                    {
+                        <tr>
+                            <td>
+                                <div class="form-check-inline">
+                                    <input type="checkbox" name="ids[]" id="ids_@row.ID" class="form-check-input list-check-box" value="@row.ID" form="fAdminList" />
+                                    <label for="ids_@row.ID">@row.ID</label>
+                                </div>
+                            </td>
+                            <td>@row.BeforeSummary</td>
+                            <td>@row.AfterSummary</td>
+                            <td>[@row.MemberID] @row.MemberEmail</td>
+                            <td>@row.CreatedAt</td>
+                            <td>
+                                <button type="button" class="btn btn-sm btn-outline-danger btn-row-delete" data-id="@row.ID">삭제</button>
+                            </td>
+                        </tr>
+                    }
+                }
+            </tbody>
+        </table>
+
+        <partial name="_Pagination" model="Model.Pagination" />
+    </div>
+</div>
+
+@section Scripts {
+    <script>
+        let searchForm = document.getElementById("fAdminSearch");
+
+        $(document).on("change", "#perPage", function () {
+            searchForm.elements["pageNum"].value = 1;
+            searchForm.submit();
+        });
+    </script>
+}

+ 104 - 0
Admin/Pages/Member/Log/Summary.cshtml.cs

@@ -0,0 +1,104 @@
+using SharedKernel.Helpers;
+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.Log;
+
+public class SummaryModel(IMediator mediator) : PageModel
+{
+    [BindProperty(SupportsGet = true)]
+    public QueryParams Query { get; set; } = new();
+
+    public sealed class QueryParams
+    {
+        [Range(1, int.MaxValue)]
+        [DisplayName("페이지 번호")]
+        public int PageNum { get; set; } = 1;
+
+        [Range(1, 100)]
+        [DisplayName("페이지 목록 수")]
+        public ushort PerPage { get; set; } = 10;
+
+        [DisplayName("검색 조건")]
+        [Range(1, 5, ErrorMessage = "{0}이(가) 올바르지 않습니다.")]
+        public int? Search { get; set; }
+
+        [DisplayName("검색어")]
+        [MaxLength(255, ErrorMessage = "{0}은(는) {1}자 이하로 입력하세요.")]
+        public string? Keyword { get; set; }
+
+        [DisplayName("시작일")]
+        public string? StartAt { get; set; }
+
+        [DisplayName("종료일")]
+        public string? EndAt { get; set; }
+    }
+
+    public int Total { get; set; } = 0;
+
+    public List<(
+        int ID,
+        int MemberID,
+        string MemberEmail,
+        string? BeforeSummary,
+        string? AfterSummary,
+        string CreatedAt
+    )> Data { get; set; } = [];
+
+    public Pagination? Pagination { get; set; }
+
+    public async Task OnGetAsync(CancellationToken ct)
+    {
+        if (!ModelState.IsValid)
+        {
+            return;
+        }
+
+        var result = await mediator.Send(new SearchSummaryChangeLogs.Query(
+            Query.PageNum,
+            Query.PerPage,
+            Query.Search,
+            Query.Keyword,
+            Query.StartAt,
+            Query.EndAt
+        ), ct);
+
+        Total = result.Total;
+        Data = [..result.List.Select(c =>
+        (
+            c.ID,
+            c.MemberID,
+            c.MemberEmail ?? "-",
+            string.IsNullOrEmpty(c.BeforeSummary) ? "-" : c.BeforeSummary,
+            string.IsNullOrEmpty(c.AfterSummary) ? "-" : c.AfterSummary,
+            c.CreatedAt.GetDateAt()
+        ))];
+
+        Pagination = new Pagination(result.Total, Query.PageNum, Query.PerPage);
+    }
+
+    public async Task<IActionResult> OnPostDeleteAsync(int[] ids, CancellationToken ct)
+    {
+        try
+        {
+            if (ids.Length == 0)
+            {
+                throw new Exception("삭제할 항목을 선택해주세요.");
+            }
+
+            await mediator.Send(new DeleteSummaryChangeLog.Command(ids), ct);
+
+            TempData["SuccessMessage"] = $"{ids.Length}건이 삭제되었습니다.";
+        }
+        catch (Exception e)
+        {
+            TempData["ErrorMessage"] = e.Message;
+        }
+
+        return RedirectToPage(Query);
+    }
+}

+ 20 - 0
Admin/Pages/Member/_navTabs.cshtml

@@ -0,0 +1,20 @@
+@{
+    var basePath = "/Member/Log/";
+}
+<ul class="nav nav-tabs">
+    <li class="nav-item">
+        <a class="nav-link @Html.IsActive(basePath + "Login")" asp-page="@(basePath + "Login")">로그인 내역</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @Html.IsActive(basePath + "Email")" asp-page="@(basePath + "Email")">이메일 변경</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @Html.IsActive(basePath + "Name")" asp-page="@(basePath + "Name")">별명 변경</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @Html.IsActive(basePath + "Summary")" asp-page="@(basePath + "Summary")">한마디 변경</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @Html.IsActive(basePath + "Intro")" asp-page="@(basePath + "Intro")">자기소개 변경</a>
+    </li>
+</ul>

+ 21 - 1
Admin/using.cs

@@ -53,4 +53,24 @@ global using GetMemberGrades = Application.Features.MemberGrade.GetAll;
 global using GetMemberGrade = Application.Features.MemberGrade.Get;
 global using GetMemberGrade = Application.Features.MemberGrade.Get;
 global using CreateMemberGrade = Application.Features.MemberGrade.Create;
 global using CreateMemberGrade = Application.Features.MemberGrade.Create;
 global using UpdateMemberGrade = Application.Features.MemberGrade.Update;
 global using UpdateMemberGrade = Application.Features.MemberGrade.Update;
-global using DeleteMemberGrade = Application.Features.MemberGrade.Delete;
+global using DeleteMemberGrade = Application.Features.MemberGrade.Delete;
+
+// 로그인 내역
+global using SearchLoginLogs = Application.Features.Member.LoginLog.Search;
+global using DeleteLoginLog = Application.Features.Member.LoginLog.Delete;
+
+// 이메일 변경 내역
+global using SearchEmailChangeLogs = Application.Features.Member.EmailChangeLog.Search;
+global using DeleteEmailChangeLog = Application.Features.Member.EmailChangeLog.Delete;
+
+// 자기소개 변경 내역
+global using SearchIntroChangeLogs = Application.Features.Member.IntroChangeLog.Search;
+global using DeleteIntroChangeLog = Application.Features.Member.IntroChangeLog.Delete;
+
+// 별명 변경 내역
+global using SearchNameChangeLogs = Application.Features.Member.NameChangeLog.Search;
+global using DeleteNameChangeLog = Application.Features.Member.NameChangeLog.Delete;
+
+// 한마디 변경 내역
+global using SearchSummaryChangeLogs = Application.Features.Member.SummaryChangeLog.Search;
+global using DeleteSummaryChangeLog = Application.Features.Member.SummaryChangeLog.Delete;

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

@@ -4,6 +4,7 @@ using Domain.Entities.Members;
 using Domain.Entities.Page;
 using Domain.Entities.Page;
 using Domain.Entities.Page.Faq;
 using Domain.Entities.Page.Faq;
 using Domain.Entities.Page.Banner;
 using Domain.Entities.Page.Banner;
+using Domain.Entities.Members.Logs;
 
 
 namespace Application.Abstractions.Data
 namespace Application.Abstractions.Data
 {
 {
@@ -18,6 +19,14 @@ namespace Application.Abstractions.Data
         DbSet<BannerItem> BannerItem { get; set;  }
         DbSet<BannerItem> BannerItem { get; set;  }
         DbSet<MemberGrade> MemberGrade { get; set;  }
         DbSet<MemberGrade> MemberGrade { get; set;  }
 
 
+        // Member Logs
+        DbSet<Member> Member { get; set; }
+        DbSet<MemberLoginLog> MemberLoginLog { get; set; }
+        DbSet<MemberEmailChangeLog> MemberEmailChangeLog { get; set; }
+        DbSet<MemberNameChangeLog> MemberNameChangeLog { get; set; }
+        DbSet<MemberSummaryChangeLog> MemberSummaryChangeLog { get; set; }
+        DbSet<MemberIntroChangeLog> MemberIntroChangeLog { get; set; }
+
         Task<int> SaveChangesAsync(CancellationToken ct = default);
         Task<int> SaveChangesAsync(CancellationToken ct = default);
     }
     }
 }
 }

+ 6 - 0
Application/Features/Member/EmailChangeLog/Delete/Command.cs

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

+ 18 - 0
Application/Features/Member/EmailChangeLog/Delete/Handler.cs

@@ -0,0 +1,18 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.EmailChangeLog.Delete;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Command>
+{
+    public async Task Handle(Command request, CancellationToken ct)
+    {
+        if (request.IDs is null || request.IDs.Length == 0)
+        {
+            return;
+        }
+
+        await db.MemberEmailChangeLog.Where(x => request.IDs.Contains(x.ID)).ExecuteDeleteAsync(ct);
+    }
+}

+ 75 - 0
Application/Features/Member/EmailChangeLog/Search/Handler.cs

@@ -0,0 +1,75 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.EmailChangeLog.Search;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+{
+    public async Task<Response> Handle(Query request, CancellationToken ct)
+    {
+        var query = db.MemberEmailChangeLog.AsNoTracking().Include(x => x.Member).AsQueryable();
+
+        if (!string.IsNullOrWhiteSpace(request.Keyword))
+        {
+            query = request.Search switch
+            {
+                1 => query.Where(x => x.MemberID.ToString().Contains(request.Keyword)),
+                2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)),
+                3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)),
+                4 => query.Where(x => x.BeforeEmail != null && x.BeforeEmail.Contains(request.Keyword)),
+                5 => query.Where(x => x.AfterEmail.Contains(request.Keyword)),
+                _ => query
+            };
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
+        {
+            query = query.Where(x => x.CreatedAt >= startAt);
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
+        {
+            query = query.Where(x => x.CreatedAt <= endAt);
+        }
+
+        var total = await query.CountAsync(ct);
+        var skip = (request.PageNum - 1) * request.PerPage;
+
+        var list = await query
+            .OrderByDescending(x => x.ID)
+            .Skip(skip)
+            .Take(request.PerPage)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberEmail = x.Member.Email,
+                x.BeforeEmail,
+                x.AfterEmail,
+                x.IpAddress,
+                x.CreatedAt
+            })
+            .ToListAsync(ct);
+
+        var rows = list
+            .Select((x, idx) => new Response.Row
+            {
+                Num = total - skip - idx,
+                ID = x.ID,
+                MemberID = x.MemberID,
+                MemberEmail = x.MemberEmail,
+                BeforeEmail = x.BeforeEmail,
+                AfterEmail = x.AfterEmail,
+                IpAddress = x.IpAddress,
+                CreatedAt = x.CreatedAt
+            })
+            .ToList();
+
+        return new Response
+        {
+            Total = total,
+            List = rows
+        };
+    }
+}

+ 13 - 0
Application/Features/Member/EmailChangeLog/Search/Query.cs

@@ -0,0 +1,13 @@
+using MediatR;
+
+namespace Application.Features.Member.EmailChangeLog.Search
+{
+    public sealed record Query(
+        int PageNum,
+        ushort PerPage,
+        int? Search = null,
+        string? Keyword = null,
+        string? StartAt = null,
+        string? EndAt = null
+    ) : IRequest<Response>;
+}

+ 20 - 0
Application/Features/Member/EmailChangeLog/Search/Response.cs

@@ -0,0 +1,20 @@
+namespace Application.Features.Member.EmailChangeLog.Search;
+
+public sealed class Response
+{
+    public int Total { get; init; }
+
+    public required IReadOnlyList<Row> List { get; init; }
+
+    public sealed class Row
+    {
+        public int Num { get; init; }
+        public int ID { get; init; }
+        public int MemberID { get; init; }
+        public required string MemberEmail { get; init; }
+        public string? BeforeEmail { get; init; }
+        public string? AfterEmail { get; init; }
+        public string? IpAddress { get; init; }
+        public required DateTime CreatedAt { get; init; }
+    }
+}

+ 5 - 0
Application/Features/Member/IntroChangeLog/Delete/Command.cs

@@ -0,0 +1,5 @@
+using MediatR;
+
+namespace Application.Features.Member.IntroChangeLog.Delete;
+
+public sealed record Command(int[] IDs) : IRequest;

+ 18 - 0
Application/Features/Member/IntroChangeLog/Delete/Handler.cs

@@ -0,0 +1,18 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.IntroChangeLog.Delete;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Command>
+{
+    public async Task Handle(Command request, CancellationToken ct)
+    {
+        if (request.IDs is null || request.IDs.Length == 0)
+        {
+            return;
+        }
+
+        await db.MemberIntroChangeLog.Where(x => request.IDs.Contains(x.ID)).ExecuteDeleteAsync(ct);
+    }
+}

+ 78 - 0
Application/Features/Member/IntroChangeLog/Search/Handler.cs

@@ -0,0 +1,78 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.IntroChangeLog.Search;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+{
+    public async Task<Response> Handle(Query request, CancellationToken ct)
+    {
+        var query = db.MemberIntroChangeLog.AsNoTracking().Include(x => x.Member).AsQueryable();
+
+        // Search filtering
+        if (!string.IsNullOrWhiteSpace(request.Keyword))
+        {
+            query = request.Search switch
+            {
+                1 => query.Where(x => x.MemberID.ToString().Contains(request.Keyword)),
+                2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)),
+                3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)),
+                4 => query.Where(x => x.BeforeIntro != null && x.BeforeIntro.Contains(request.Keyword)),
+                5 => query.Where(x => x.AfterIntro != null && x.AfterIntro.Contains(request.Keyword)),
+                _ => query
+            };
+        }
+
+        // Date filtering
+        if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
+        {
+            query = query.Where(x => x.CreatedAt >= startAt);
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
+        {
+            query = query.Where(x => x.CreatedAt <= endAt);
+        }
+
+        var total = await query.CountAsync(ct);
+        var skip = (request.PageNum - 1) * request.PerPage;
+
+        var list = await query
+            .OrderByDescending(x => x.ID)
+            .Skip(skip)
+            .Take(request.PerPage)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberEmail = x.Member.Email,
+                MemberName = x.Member.Email,
+                x.BeforeIntro,
+                x.AfterIntro,
+                x.IpAddress,
+                x.CreatedAt
+            })
+            .ToListAsync(ct);
+
+        var rows = list.Select((x, idx) => new Response.Row
+        {
+            Num = total - skip - idx,
+            ID = x.ID,
+            MemberID = x.MemberID,
+            MemberEmail = x.MemberEmail,
+            MemberName = x.MemberName,
+            BeforeIntro = x.BeforeIntro,
+            AfterIntro = x.AfterIntro,
+            IpAddress = x.IpAddress,
+            CreatedAt = x.CreatedAt
+        })
+        .ToList();
+
+        return new Response
+        {
+            Total = total,
+            List = rows
+        };
+    }
+}

+ 13 - 0
Application/Features/Member/IntroChangeLog/Search/Query.cs

@@ -0,0 +1,13 @@
+using MediatR;
+
+namespace Application.Features.Member.IntroChangeLog.Search
+{
+    public sealed record Query(
+        int PageNum,
+        ushort PerPage,
+        int? Search = null,
+        string? Keyword = null,
+        string? StartAt = null,
+        string? EndAt = null
+    ) : IRequest<Response>;
+}

+ 21 - 0
Application/Features/Member/IntroChangeLog/Search/Response.cs

@@ -0,0 +1,21 @@
+namespace Application.Features.Member.IntroChangeLog.Search;
+
+public sealed class Response
+{
+    public int Total { get; init; }
+
+    public required IReadOnlyList<Row> List { get; init; }
+
+    public sealed class Row
+    {
+        public int Num { get; init; }
+        public int ID { get; init; }
+        public int MemberID { get; init; }
+        public required string MemberEmail { get; init; }
+        public string? MemberName { get; init; }
+        public string? BeforeIntro { get; init; }
+        public string? AfterIntro { get; init; }
+        public string? IpAddress { get; init; }
+        public required DateTime CreatedAt { get; init; }
+    }
+}

+ 6 - 0
Application/Features/Member/LoginLog/Delete/Command.cs

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

+ 18 - 0
Application/Features/Member/LoginLog/Delete/Handler.cs

@@ -0,0 +1,18 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.LoginLog.Delete;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Command>
+{
+    public async Task Handle(Command request, CancellationToken ct)
+    {
+        if (request.IDs is null || request.IDs.Length == 0)
+        {
+            return;
+        }
+
+        await db.MemberLoginLog.Where(x => request.IDs.Contains(x.ID)).ExecuteDeleteAsync(ct);
+    }
+}

+ 94 - 0
Application/Features/Member/LoginLog/Get/Handler.cs

@@ -0,0 +1,94 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.LoginLog.Get;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response?>
+{
+    public async Task<Response?> Handle(Query request, CancellationToken ct)
+    {
+        var entity = await db.MemberLoginLog
+            .AsNoTracking()
+            .Include(x => x.Member)
+            .Where(x => x.ID == request.Id)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberName = x.Member.Name,
+                x.Account,
+                x.Success,
+                x.Reason,
+                x.Referer,
+                x.Url,
+                x.IpAddress,
+                x.UserAgent,
+                x.CreatedAt
+            })
+            .FirstOrDefaultAsync(ct);
+
+        if (entity == null) return null;
+
+        return new Response
+        {
+            ID = entity.ID,
+            MemberID = entity.MemberID,
+            MemberName = entity.MemberName,
+            Account = entity.Account,
+            Success = entity.Success,
+            Reason = entity.Reason,
+            Referer = entity.Referer,
+            Url = entity.Url,
+            IpAddress = entity.IpAddress,
+            UserAgent = entity.UserAgent,
+            Browser = ParseUserAgent(entity.UserAgent, "browser"),
+            OS = ParseUserAgent(entity.UserAgent, "os"),
+            Device = ParseUserAgent(entity.UserAgent, "device"),
+            CreatedAt = entity.CreatedAt
+        };
+    }
+
+    private static string? ParseUserAgent(string? userAgent, string type)
+    {
+        if (string.IsNullOrWhiteSpace(userAgent)) return null;
+
+        return type switch
+        {
+            "browser" => ExtractBrowser(userAgent),
+            "os" => ExtractOS(userAgent),
+            "device" => ExtractDevice(userAgent),
+            _ => null
+        };
+    }
+
+    private static string ExtractBrowser(string ua)
+    {
+        if (ua.Contains("Edg/")) return "Edge";
+        if (ua.Contains("Chrome/")) return "Chrome";
+        if (ua.Contains("Firefox/")) return "Firefox";
+        if (ua.Contains("Safari/") && !ua.Contains("Chrome")) return "Safari";
+        if (ua.Contains("MSIE") || ua.Contains("Trident/")) return "IE";
+        return "Unknown";
+    }
+
+    private static string ExtractOS(string ua)
+    {
+        if (ua.Contains("Windows NT 10")) return "Windows 10";
+        if (ua.Contains("Windows NT 6.3")) return "Windows 8.1";
+        if (ua.Contains("Windows NT 6.1")) return "Windows 7";
+        if (ua.Contains("Windows")) return "Windows";
+        if (ua.Contains("Mac OS X")) return "macOS";
+        if (ua.Contains("Android")) return "Android";
+        if (ua.Contains("iPhone") || ua.Contains("iPad")) return "iOS";
+        if (ua.Contains("Linux")) return "Linux";
+        return "Unknown";
+    }
+
+    private static string ExtractDevice(string ua)
+    {
+        if (ua.Contains("Mobile") || ua.Contains("Android") && !ua.Contains("Tablet")) return "Mobile";
+        if (ua.Contains("Tablet") || ua.Contains("iPad")) return "Tablet";
+        return "Desktop";
+    }
+}

+ 6 - 0
Application/Features/Member/LoginLog/Get/Query.cs

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

+ 19 - 0
Application/Features/Member/LoginLog/Get/Response.cs

@@ -0,0 +1,19 @@
+namespace Application.Features.Member.LoginLog.Get;
+
+public sealed class Response
+{
+    public int ID { get; init; }
+    public int? MemberID { get; init; }
+    public string? MemberName { get; init; }
+    public required string Account { get; init; }
+    public bool Success { get; init; }
+    public string? Reason { get; init; }
+    public string? Referer { get; init; }
+    public string? Url { get; init; }
+    public string? IpAddress { get; init; }
+    public string? UserAgent { get; init; }
+    public string? Browser { get; init; }
+    public string? OS { get; init; }
+    public string? Device { get; init; }
+    public required DateTime CreatedAt { get; init; }
+}

+ 125 - 0
Application/Features/Member/LoginLog/Search/Handler.cs

@@ -0,0 +1,125 @@
+using SharedKernel.Extensions;
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.LoginLog.Search;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+{
+    public async Task<Response> Handle(Query request, CancellationToken ct)
+    {
+        var query = db.MemberLoginLog.AsNoTracking().Include(x => x.Member).AsQueryable();
+
+        if (!string.IsNullOrWhiteSpace(request.Keyword))
+        {
+            query = request.Search switch
+            {
+                1 => query.Where(x => x.MemberID.HasValue && x.MemberID.Value.ToString().Contains(request.Keyword)),
+                2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)),
+                3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)),
+                _ => query
+            };
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
+        {
+            query = query.Where(x => x.CreatedAt >= startAt);
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
+        {
+            query = query.Where(x => x.CreatedAt <= endAt);
+        }
+
+        var total = await query.CountAsync(ct);
+        var skip = (request.PageNum - 1) * request.PerPage;
+
+        var list = await query
+            .OrderByDescending(x => x.ID)
+            .Skip(skip)
+            .Take(request.PerPage)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberName = x.Member != null ? x.Member.Name : null,
+                x.Account,
+                x.Success,
+                x.Reason,
+                x.IpAddress,
+                x.UserAgent,
+                x.CreatedAt
+            })
+            .ToListAsync(ct);
+
+        var rows = list.Select((x, idx) => new Response.Row
+        {
+            Num = total - skip - idx,
+            ID = x.ID,
+            MemberID = x.MemberID,
+            MemberName = x.MemberName,
+            Account = x.Account,
+            Success = x.Success,
+            Reason = x.Reason,
+            IpAddress = x.IpAddress,
+            UserAgent = x.UserAgent,
+            Browser = ParseUserAgent(x.UserAgent, "browser"),
+            OS = ParseUserAgent(x.UserAgent, "os"),
+            Device = ParseUserAgent(x.UserAgent, "device"),
+            CreatedAt = x.CreatedAt
+        }).ToList();
+
+        return new Response
+        {
+            Total = total,
+            List = rows
+        };
+    }
+
+    private static string? ParseUserAgent(string? userAgent, string type)
+    {
+        if (string.IsNullOrWhiteSpace(userAgent))
+        {
+            return null;
+        }
+
+        return type switch
+        {
+            "browser" => ExtractBrowser(userAgent),
+            "os" => ExtractOS(userAgent),
+            "device" => ExtractDevice(userAgent),
+            _ => null
+        };
+    }
+
+    private static string ExtractBrowser(string ua)
+    {
+        if (ua.Contains("Edg/")) return "Edge";
+        if (ua.Contains("Chrome/")) return "Chrome";
+        if (ua.Contains("Firefox/")) return "Firefox";
+        if (ua.Contains("Safari/") && !ua.Contains("Chrome")) return "Safari";
+        if (ua.Contains("MSIE") || ua.Contains("Trident/")) return "IE";
+        return "Unknown";
+    }
+
+    private static string ExtractOS(string ua)
+    {
+        if (ua.Contains("Windows NT 10")) return "Windows 10";
+        if (ua.Contains("Windows NT 6.3")) return "Windows 8.1";
+        if (ua.Contains("Windows NT 6.1")) return "Windows 7";
+        if (ua.Contains("Windows")) return "Windows";
+        if (ua.Contains("Mac OS X")) return "macOS";
+        if (ua.Contains("Android")) return "Android";
+        if (ua.Contains("iPhone") || ua.Contains("iPad")) return "iOS";
+        if (ua.Contains("Linux")) return "Linux";
+        return "Unknown";
+    }
+
+    private static string ExtractDevice(string ua)
+    {
+        if (ua.Contains("Mobile") || ua.Contains("Android") && !ua.Contains("Tablet")) return "Mobile";
+        if (ua.Contains("Tablet") || ua.Contains("iPad")) return "Tablet";
+        return "Desktop";
+    }
+}

+ 13 - 0
Application/Features/Member/LoginLog/Search/Query.cs

@@ -0,0 +1,13 @@
+using MediatR;
+
+namespace Application.Features.Member.LoginLog.Search
+{
+    public sealed record Query(
+        int PageNum,
+        ushort PerPage,
+        int? Search = null,
+        string? Keyword = null,
+        string? StartAt = null,
+        string? EndAt = null
+    ) : IRequest<Response>;
+}

+ 25 - 0
Application/Features/Member/LoginLog/Search/Response.cs

@@ -0,0 +1,25 @@
+namespace Application.Features.Member.LoginLog.Search;
+
+public sealed class Response
+{
+    public int Total { get; init; }
+
+    public required IReadOnlyList<Row> List { get; init; }
+
+    public sealed class Row
+    {
+        public int Num { get; init; }
+        public int ID { get; init; }
+        public int? MemberID { get; init; }
+        public string? MemberName { get; init; }
+        public required string Account { get; init; }
+        public bool Success { get; init; }
+        public string? Reason { get; init; }
+        public string? IpAddress { get; init; }
+        public string? UserAgent { get; init; }
+        public string? Browser { get; init; }
+        public string? OS { get; init; }
+        public string? Device { get; init; }
+        public required DateTime CreatedAt { get; init; }
+    }
+}

+ 6 - 0
Application/Features/Member/NameChangeLog/Delete/Command.cs

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

+ 18 - 0
Application/Features/Member/NameChangeLog/Delete/Handler.cs

@@ -0,0 +1,18 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.NameChangeLog.Delete;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Command>
+{
+    public async Task Handle(Command request, CancellationToken ct)
+    {
+        if (request.IDs is null || request.IDs.Length == 0)
+        {
+            return;
+        }
+
+        await db.MemberNameChangeLog.Where(x => request.IDs.Contains(x.ID)).ExecuteDeleteAsync(ct);
+    }
+}

+ 74 - 0
Application/Features/Member/NameChangeLog/Search/Handler.cs

@@ -0,0 +1,74 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.NameChangeLog.Search;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+{
+    public async Task<Response> Handle(Query request, CancellationToken ct)
+    {
+        var query = db.MemberNameChangeLog.AsNoTracking().Include(x => x.Member).AsQueryable();
+
+        if (!string.IsNullOrWhiteSpace(request.Keyword))
+        {
+            query = request.Search switch
+            {
+                1 => query.Where(x => x.MemberID.ToString().Contains(request.Keyword)),
+                2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)),
+                3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)),
+                4 => query.Where(x => x.BeforeName != null && x.BeforeName.Contains(request.Keyword)),
+                5 => query.Where(x => x.AfterName != null && x.AfterName.Contains(request.Keyword)),
+                _ => query
+            };
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
+        {
+            query = query.Where(x => x.CreatedAt >= startAt);
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
+        {
+            query = query.Where(x => x.CreatedAt <= endAt);
+        }
+
+        var total = await query.CountAsync(ct);
+        var skip = (request.PageNum - 1) * request.PerPage;
+
+        var list = await query
+            .OrderByDescending(x => x.ID)
+            .Skip(skip)
+            .Take(request.PerPage)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberEmail = x.Member.Email,
+                x.BeforeName,
+                x.AfterName,
+                x.IpAddress,
+                x.CreatedAt
+            })
+            .ToListAsync(ct);
+
+        var rows = list.Select((x, idx) => new Response.Row
+        {
+            Num = total - skip - idx,
+            ID = x.ID,
+            MemberID = x.MemberID,
+            MemberEmail = x.MemberEmail,
+            BeforeName = x.BeforeName,
+            AfterName = x.AfterName,
+            IpAddress = x.IpAddress,
+            CreatedAt = x.CreatedAt
+        })
+        .ToList();
+
+        return new Response
+        {
+            Total = total,
+            List = rows
+        };
+    }
+}

+ 13 - 0
Application/Features/Member/NameChangeLog/Search/Query.cs

@@ -0,0 +1,13 @@
+using MediatR;
+
+namespace Application.Features.Member.NameChangeLog.Search
+{
+    public sealed record Query(
+        int PageNum,
+        ushort PerPage,
+        int? Search = null,
+        string? Keyword = null,
+        string? StartAt = null,
+        string? EndAt = null
+    ) : IRequest<Response>;
+}

+ 20 - 0
Application/Features/Member/NameChangeLog/Search/Response.cs

@@ -0,0 +1,20 @@
+namespace Application.Features.Member.NameChangeLog.Search;
+
+public sealed class Response
+{
+    public int Total { get; init; }
+
+    public required IReadOnlyList<Row> List { get; init; }
+
+    public sealed class Row
+    {
+        public int Num { get; init; }
+        public int ID { get; init; }
+        public int MemberID { get; init; }
+        public required string MemberEmail { get; init; }
+        public string? BeforeName { get; init; }
+        public string? AfterName { get; init; }
+        public string? IpAddress { get; init; }
+        public required DateTime CreatedAt { get; init; }
+    }
+}

+ 6 - 0
Application/Features/Member/SummaryChangeLog/Delete/Command.cs

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

+ 18 - 0
Application/Features/Member/SummaryChangeLog/Delete/Handler.cs

@@ -0,0 +1,18 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.SummaryChangeLog.Delete;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Command>
+{
+    public async Task Handle(Command request, CancellationToken ct)
+    {
+        if (request.IDs is null || request.IDs.Length == 0)
+        {
+            return;
+        }
+
+        await db.MemberSummaryChangeLog.Where(x => request.IDs.Contains(x.ID)).ExecuteDeleteAsync(ct);
+    }
+}

+ 74 - 0
Application/Features/Member/SummaryChangeLog/Search/Handler.cs

@@ -0,0 +1,74 @@
+using Application.Abstractions.Data;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+
+namespace Application.Features.Member.SummaryChangeLog.Search;
+
+public sealed class Handler(IAppDbContext db) : IRequestHandler<Query, Response>
+{
+    public async Task<Response> Handle(Query request, CancellationToken ct)
+    {
+        var query = db.MemberSummaryChangeLog.AsNoTracking().Include(x => x.Member).AsQueryable();
+
+        if (!string.IsNullOrWhiteSpace(request.Keyword))
+        {
+            query = request.Search switch
+            {
+                1 => query.Where(x => x.MemberID.ToString().Contains(request.Keyword)),
+                2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)),
+                3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)),
+                4 => query.Where(x => x.BeforeSummary != null && x.BeforeSummary.Contains(request.Keyword)),
+                5 => query.Where(x => x.AfterSummary != null && x.AfterSummary.Contains(request.Keyword)),
+                _ => query
+            };
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
+        {
+            query = query.Where(x => x.CreatedAt >= startAt);
+        }
+
+        if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
+        {
+            query = query.Where(x => x.CreatedAt <= endAt);
+        }
+
+        var total = await query.CountAsync(ct);
+        var skip = (request.PageNum - 1) * request.PerPage;
+
+        var list = await query
+            .OrderByDescending(x => x.ID)
+            .Skip(skip)
+            .Take(request.PerPage)
+            .Select(x => new
+            {
+                x.ID,
+                x.MemberID,
+                MemberEmail = x.Member.Email,
+                x.BeforeSummary,
+                x.AfterSummary,
+                x.IpAddress,
+                x.CreatedAt
+            })
+            .ToListAsync(ct);
+
+        var rows = list.Select((x, idx) => new Response.Row
+        {
+            Num = total - skip - idx,
+            ID = x.ID,
+            MemberID = x.MemberID,
+            MemberEmail = x.MemberEmail,
+            BeforeSummary = x.BeforeSummary,
+            AfterSummary = x.AfterSummary,
+            IpAddress = x.IpAddress,
+            CreatedAt = x.CreatedAt
+        })
+        .ToList();
+
+        return new Response
+        {
+            Total = total,
+            List = rows
+        };
+    }
+}

+ 13 - 0
Application/Features/Member/SummaryChangeLog/Search/Query.cs

@@ -0,0 +1,13 @@
+using MediatR;
+
+namespace Application.Features.Member.SummaryChangeLog.Search
+{
+    public sealed record Query(
+        int PageNum,
+        ushort PerPage,
+        int? Search = null,
+        string? Keyword = null,
+        string? StartAt = null,
+        string? EndAt = null
+    ) : IRequest<Response>;
+}

+ 20 - 0
Application/Features/Member/SummaryChangeLog/Search/Response.cs

@@ -0,0 +1,20 @@
+namespace Application.Features.Member.SummaryChangeLog.Search;
+
+public sealed class Response
+{
+    public int Total { get; init; }
+
+    public required IReadOnlyList<Row> List { get; init; }
+
+    public sealed class Row
+    {
+        public int Num { get; init; }
+        public int ID { get; init; }
+        public int MemberID { get; init; }
+        public string? MemberEmail { get; init; }
+        public string? BeforeSummary { get; init; }
+        public string? AfterSummary { get; init; }
+        public string? IpAddress { get; init; }
+        public required DateTime CreatedAt { get; init; }
+    }
+}

+ 16 - 7
Infrastructure/Persistence/AppDbContext.cs

@@ -1,6 +1,7 @@
 using Application.Abstractions.Data;
 using Application.Abstractions.Data;
 using Domain.Entities.Common;
 using Domain.Entities.Common;
 using Domain.Entities.Members;
 using Domain.Entities.Members;
+using Domain.Entities.Members.Logs;
 using Domain.Entities.Page;
 using Domain.Entities.Page;
 using Domain.Entities.Page.Banner;
 using Domain.Entities.Page.Banner;
 using Domain.Entities.Page.Faq;
 using Domain.Entities.Page.Faq;
@@ -13,13 +14,21 @@ namespace Infrastructure.Persistence
         public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
         public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
 
 
         public DbSet<Config> Config { get; set; }
         public DbSet<Config> Config { get; set; }
-        public DbSet<Document> Document { get; set; } = null!;
-        public DbSet<Popup> Popup { get; set; } = null!;
-        public DbSet<FaqCategory> FaqCategory { get; set; } = null!;
-        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!;
+        public DbSet<Document> Document { get; set; }
+        public DbSet<Popup> Popup { get; set; }
+        public DbSet<FaqCategory> FaqCategory { get; set; }
+        public DbSet<FaqItem> FaqItem { get; set; }
+        public DbSet<BannerPosition> BannerPosition { get; set; }
+        public DbSet<BannerItem> BannerItem { get; set; }
+        public DbSet<MemberGrade> MemberGrade { get; set; }
+
+        // Member Logs
+        public DbSet<Member> Member { get; set; }
+        public DbSet<MemberLoginLog> MemberLoginLog { get; set; }
+        public DbSet<MemberEmailChangeLog> MemberEmailChangeLog { get; set; }
+        public DbSet<MemberNameChangeLog> MemberNameChangeLog { get; set; }
+        public DbSet<MemberSummaryChangeLog> MemberSummaryChangeLog { get; set; }
+        public DbSet<MemberIntroChangeLog> MemberIntroChangeLog { get; set; }
 
 
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
         {

+ 0 - 1
Infrastructure/Persistence/Configurations/Members/Logs/MemberLoginLogConfiguration.cs

@@ -9,7 +9,6 @@ public sealed class MemberLoginLogConfiguration : IEntityTypeConfiguration<Membe
     public void Configure(EntityTypeBuilder<MemberLoginLog> builder)
     public void Configure(EntityTypeBuilder<MemberLoginLog> builder)
     {
     {
         builder.HasOne(x => x.Member).WithMany().HasForeignKey(x => x.MemberID).OnDelete(DeleteBehavior.SetNull);
         builder.HasOne(x => x.Member).WithMany().HasForeignKey(x => x.MemberID).OnDelete(DeleteBehavior.SetNull);
-
         builder.HasIndex(x => x.MemberID);
         builder.HasIndex(x => x.MemberID);
         builder.HasIndex(x => x.Account);
         builder.HasIndex(x => x.Account);
         builder.HasIndex(x => new { x.MemberID, x.Success });
         builder.HasIndex(x => new { x.MemberID, x.Success });

+ 2276 - 0
Infrastructure/Persistence/Migrations/20260205091731_PendingChanges.Designer.cs

@@ -0,0 +1,2276 @@
+// <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.Migrations.AppDb
+{
+    [DbContext(typeof(AppDbContext))]
+    [Migration("20260205091731_PendingChanges")]
+    partial class PendingChanges
+    {
+        /// <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<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
+        }
+    }
+}

+ 22 - 0
Infrastructure/Persistence/Migrations/20260205091731_PendingChanges.cs

@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Infrastructure.Migrations.AppDb
+{
+    /// <inheritdoc />
+    public partial class PendingChanges : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+
+        }
+    }
+}

+ 2276 - 0
Infrastructure/Persistence/Migrations/20260205103258_AddMemberLogs.Designer.cs

@@ -0,0 +1,2276 @@
+// <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.Migrations.AppDb
+{
+    [DbContext(typeof(AppDbContext))]
+    [Migration("20260205103258_AddMemberLogs")]
+    partial class AddMemberLogs
+    {
+        /// <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<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
+        }
+    }
+}

+ 22 - 0
Infrastructure/Persistence/Migrations/20260205103258_AddMemberLogs.cs

@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Infrastructure.Migrations.AppDb
+{
+    /// <inheritdoc />
+    public partial class AddMemberLogs : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+
+        }
+    }
+}