Azunt.Components 패키지 만들기

  • 5 minutes to read

Reusable Blazor UI Component Library (Step-by-Step Guide)

이 문서는 Blazor에서 공통 UI 컴포넌트(SearchBox, SortOrderArrow, Pager 등)를 RCL(Razor Class Library)로 만들어 NuGet 패키지로 배포하고, 실제 Blazor 앱에서 사용하는 전 과정을 안내합니다.


GitHub에 리포지토리 만들기: Azunt.Components

이 단계에서는 만든 RCL 프로젝트를 GitHub에 업로드하고 오픈소스로 관리하는 방법을 안내합니다. URL은 다음과 같습니다:

🔗 https://github.com/VisualAcademy/Azunt.Components


✅ 1단계: Git 초기화

git init
git add .
git commit -m "Initialize Azunt.Components project"

✅ 2단계: GitHub 리포지토리 생성

  1. https://github.com/VisualAcademy 로 이동
  2. Azunt.Components라는 이름으로 New repository 생성
  3. 공개(Public) 여부 선택 후 생성

✅ 3단계: 원격 연결 및 푸시

git remote add origin https://github.com/VisualAcademy/Azunt.Components.git
git branch -M main
git push -u origin main

✅ 4단계: .gitignoreREADME.md 추가 (선택)

프로젝트 루트에 다음 파일들을 추가하세요:

.gitignore

bin/
obj/
*.user
*.suo
*.userosscache
*.sln.docstates

README.md

# Azunt.Components

Reusable Blazor UI Components (SearchBox, SortOrderArrow, Pager)  
Packaged as a Razor Class Library (RCL) and published on NuGet.

NuGet: [https://www.nuget.org/packages/Azunt.Components](https://www.nuget.org/packages/Azunt.Components)
git add .gitignore README.md
git commit -m "Add README and .gitignore"
git push

✅ 5단계: GitHub Topics 및 설명 추가

GitHub 리포지토리 상단에서 다음 정보 입력:

  • Description: Reusable Blazor UI Component Library (Search, Sort, Paging)
  • Topics: blazor, razor-components, nuget, ui-library, azunt

1. RCL(Razor Class Library) 프로젝트 생성

dotnet new razorclasslib -n Azunt.Components
cd Azunt.Components

.csproj 수정:

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <RazorLangVersion>8.0</RazorLangVersion>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
  </ItemGroup>

</Project>

🔧 net8.0을 사용해야 최신 Blazor 기능과 호환됩니다.


2. 프로젝트 구조 만들기

mkdir -p Paging Search Sorting
touch _Imports.razor
Azunt.Components/
├─ Paging/
│  ├─ Pager.razor
│  └─ PagerBase.cs
├─ Search/
│  ├─ SearchBox.razor
│  └─ SearchBox.razor.cs
├─ Sorting/
│  └─ SortOrderArrow.razor
├─ Azunt.Components.csproj
├─ _Imports.razor

3. 페이징 컴포넌트 만들기 (Paging)

Paging/PagerBase.cs

namespace Azunt.Components.Paging;

public class PagerBase
{
    public string Url { get; set; } = "";
    public int PageCount { get; set; } = 5;
    public int RecordCount { get; set; } = 50;
    public int PageSize { get; set; } = 10;
    public int PageIndex { get; set; } = 0;
    public int PageNumber { get; set; } = 1;
    public int PagerButtonCount { get; set; } = 3;

    public bool SearchMode { get; set; } = false;
    public string SearchField { get; set; } = "";
    public string SearchQuery { get; set; } = "";
}

Paging/Pager.razor

@using Azunt.Components.Paging
@namespace Azunt.Components.Paging

<div class="d-flex">
    <ul class="pagination pagination-sm mx-auto">
        @if (Model.PageNumber > 1)
        {
            <li class="page-item"><a class="page-link" @onclick="@(() => PagerClicked(1))">FIRST</a></li>
            <li class="page-item"><a class="page-link" @onclick="@(() => PagerClicked(Model.PageNumber - 1))">PREV</a></li>
        }
        else
        {
            <li class="page-item disabled"><span class="page-link">FIRST</span></li>
            <li class="page-item disabled"><span class="page-link">PREV</span></li>
        }

        @{
            int start = (Model.PageIndex / Model.PagerButtonCount) * Model.PagerButtonCount + 1;
            int end = Math.Min(start + Model.PagerButtonCount - 1, Model.PageCount);
        }

        @for (int i = start; i <= end; i++)
        {
            if (i == Model.PageNumber)
            {
                <li class="page-item active"><span class="page-link">@i</span></li>
            }
            else
            {
                <li class="page-item"><a class="page-link" @onclick="@(() => PagerClicked(i))">@i</a></li>
            }
        }

        @if (Model.PageNumber < Model.PageCount)
        {
            <li class="page-item"><a class="page-link" @onclick="@(() => PagerClicked(Model.PageNumber + 1))">NEXT</a></li>
            <li class="page-item"><a class="page-link" @onclick="@(() => PagerClicked(Model.PageCount))">LAST</a></li>
        }
        else
        {
            <li class="page-item disabled"><span class="page-link">NEXT</span></li>
            <li class="page-item disabled"><span class="page-link">LAST</span></li>
        }
    </ul>
</div>

@code {
    [Parameter, EditorRequired] public PagerBase Model { get; set; } = default!;
    [Parameter] public EventCallback<int> PageIndexChanged { get; set; }

    protected override Task OnParametersSetAsync()
    {
        Model.PageCount = (Model.RecordCount == 0) ? 1 : Convert.ToInt32(Math.Ceiling(Model.RecordCount / (double)Model.PageSize));
        return base.OnParametersSetAsync();
    }

    private void PagerClicked(int pageNumber)
    {
        Model.PageNumber = pageNumber;
        Model.PageIndex = pageNumber - 1;
        PageIndexChanged.InvokeAsync(Model.PageIndex);
    }
}

Search/SearchBox.razor

@namespace Azunt.Components.Search

<div class="input-group mb-3">
    <input class="form-control form-control-sm"
           type="search"
           placeholder="Search..."
           aria-describedby="btnSearch"
           @attributes="AdditionalAttributes"
           @bind="SearchQuery"
           @bind:event="oninput" />

    <div class="input-group-append">
        <button class="btn btn-sm btn-success" type="submit" @onclick="Search" id="btnSearch">Search</button>
    </div>
</div>

Search/SearchBox.razor.cs

using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Timers;

namespace Azunt.Components.Search;

public partial class SearchBox : ComponentBase, IDisposable
{
    private string searchQuery = "";
    private Timer? debounceTimer;

    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();

    [Parameter]
    public EventCallback<string> SearchQueryChanged { get; set; }

    [Parameter]
    public int Debounce { get; set; } = 300;

    public string SearchQuery
    {
        get => searchQuery;
        set
        {
            searchQuery = value;
            debounceTimer?.Stop();
            debounceTimer?.Start();
        }
    }

    protected override void OnInitialized()
    {
        debounceTimer = new Timer { Interval = Debounce, AutoReset = false };
        debounceTimer.Elapsed += SearchHandler!;
    }

    protected void Search() => SearchQueryChanged.InvokeAsync(SearchQuery);

    protected async void SearchHandler(object? source, ElapsedEventArgs e)
    {
        await InvokeAsync(() => SearchQueryChanged.InvokeAsync(SearchQuery));
    }

    public void Dispose() => debounceTimer?.Dispose();
}

5. 정렬 화살표 컴포넌트 만들기 (Sorting)

Sorting/SortOrderArrow.razor

@namespace Azunt.Components.Sorting

<span style="color: silver; margin-left: 7px; font-weight: bold; float: right;">@arrow</span>

@code {
    [Parameter] public string SortColumn { get; set; } = "";
    [Parameter] public string SortOrder { get; set; } = "";

    private string arrow = "↕";

    protected override void OnParametersSet()
    {
        if (string.IsNullOrWhiteSpace(SortOrder))
        {
            arrow = "↕";
        }
        else if (SortOrder.Contains(SortColumn) && SortOrder.Contains("Desc"))
        {
            arrow = "↓";
        }
        else if (SortOrder.Contains(SortColumn))
        {
            arrow = "↑";
        }
        else
        {
            arrow = " ";
        }
    }
}

6. NuGet 패키지로 만들기

NuGet 패키지 생성

패키지 생성

dotnet pack -c Release

NuGet 게시 (API Key 필요)

dotnet nuget push bin/Release/Azunt.Components.X.Y.Z.nupkg -k {API_KEY} -s https://api.nuget.org/v3/index.json

7. Blazor 앱에서 사용하기

  1. NuGet에서 Azunt.Components 설치
  2. _Imports.razor에 다음 추가:
@using Azunt.Components.Search
@using Azunt.Components.Sorting
@using Azunt.Components.Paging
  1. Razor 페이지에서 사용:
<SearchBox SearchQueryChanged="OnSearch" />
<SortOrderArrow SortColumn="Name" SortOrder="@sortOrder" />
<Pager Model="pagerModel" PageIndexChanged="OnPageChanged" />
VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com