Electron.NET과 함께 Blazor 데스크톱 앱 빌드하기 > 블로그 & Tips

본문 바로가기

ComponentOne

블로그 & Tips

Electron.NET과 함께 Blazor 데스크톱 앱 빌드하기

페이지 정보

작성자 GrapeCity 작성일 21-05-27 14:46 조회 239회 댓글 0건

본문

저는 이전 블로그 게시물에서 오픈 소스 도구인 Electron.NET을 사용하여 Windows, macOS, Linux 플랫폼을 위한 크로스 플랫폼 데스크톱 응용 프로그램을 구현하는 방법에 대해 썼습니다.


임베디드 ASP.NET Core 웹사이트에서 표준 Electron 응용 프로그램에 대한 래퍼를 제공하여 Electron.NET은 C# 개발자가 JavaScript로 코딩할 필요 없이 여러 플랫폼을 대상으로 할 수 있습니다.


이 블로그 게시물에서는 같은 기법을 적용하여 Blazor 데스크톱 응용 프로그램을 만드는 방법을 설명합니다. 여기서 다루는 주제는 다음과 같습니다.


  • Electron.NET을 사용하도록 기본 Blazor 서버 응용 프로그램 수정

  • Visual Studio Code를 사용하여 Blazor 페이지 디버깅

  • 메시지 상자 등과 같은 기본 UI 요소 구현

  • 타사 Blazor 컨트롤 추가

  • 기타 플랫폼용 배포 미디어 빌드



배경


Electron은 Chromium 렌더링 엔진 및 Node.js 런타임과 같은 웹 기술을 사용하는 데스크톱 응용 프로그램 개발을 지원하는 프레임워크입니다. 지원되는 운영 체제는 Windows, macOS, Linux 등입니다. Electron은 HTML, CSS, JavaScript 등 익숙한 표준을 활용합니다.


Electron.NET을 통해 .NET 개발자는 C#을 사용하여 기본 Electron API를 호출할 수 있습니다 Electron.NET은 다음 두 가지 컴포넌트로 구성됩니다.

  • ASP.NET Core 또는 Blazor 프로젝트에 Electron API를 추가하는 NuGet 패키지

  • Windows, macOS 및 Linux 플랫폼용 응용 프로그램을 빌드 및 실행하는 .NET Core 명령줄 확장


Electron.NET을 사용하려면 다음 소프트웨어를 미리 설치해야 합니다.


명령줄에서 정식 Blazor 응용 프로그램을 빌드하여 시작해 보겠습니다.


Blazor 서버 응용 프로그램 만들기


이 연습에서는 Mac에서 실행 중인 Visual Studio Code를 사용합니다. 먼저 터미널 창을 열고 다음 명령을 실행하여 BlazorApp이라는 새 프로젝트를 만듭니다.

mkdir BlazorApp  
cd BlazorApp  
dotnet new blazorserver  
code .  


Visual Studio Code에서 메시지가 표시되면 "예"를 선택하여 프로젝트의 필수 자산을 로드합니다.


F5 키를 눌러 응용 프로그램을 빌드하고 실행한 다음 localhost:5001에서 브라우저를 열어 기본 Blazor 페이지를 봅니다. 페이지를 닫고 VS Code로 돌아간 후 디버그를 중지합니다.


Electron 활용!


이제 표준 Blazor 프로젝트를 데스크톱 Electron 응용 프로그램으로 변환해 보겠습니다.


이 작업에는 NuGet 패키지를 프로젝트 파일에 추가, 몇 가지 초기화 코드 삽입, 빌드 수행을 위한 명령줄 도구 설치 등이 포함됩니다.


먼저 BlazorApp.csproj 파일을 열고 nuget.org에 호스팅된 Electron.NET API용 패키지 참조를 삽입합니다.

<ItemGroup>  
<PackageReference Include="ElectronNET.API" Version="11.5.1" />  
</ItemGroup>  


파일을 저장한 후, VS Code에서 메시지가 표시되면 패키지를 복원합니다.

패키지를 복원하면 Intellisense에 즉시 액세스하여 코드를 이후에 수정할 수 있습니다.

그런 다음, Program.cs를 편집한 후 새로 추가된 패키지 문을 사용하여 다음을 삽입합니다.

using ElectronNET.API;


정적 메서드 CreateHostBuilder를 찾아 UseStartup을 호출하기 전에 다음과 같은 2개 행을 삽입합니다.

webBuilder.UseElectron(args);  
webBuilder.UseEnvironment("Development");  


첫 번째 행은 필수입니다. 두 번째 행은 세부 오류 메시지를 표시할 수 있으므로 개발 과정에서 편리합니다.


Startup.cs를 편집한 후 문을 사용하여 다음을 삽입합니다.

using ElectronNET.API; 

 

Configure 메서드를 찾아 본문 끝에 다음 행을 추가합니다.

if (HybridSupport.IsElectronActive)  
{  
  CreateWindow();  
}


마지막으로 Startup 클래스에 다음 메서드를 추가하여 기본 Electron 창을 만듭니다.

private async void CreateWindow()  
{  
  var window = await Electron.WindowManager.CreateWindowAsync();  
  window.OnClosed += () => {  
      Electron.App.Quit();  
  };  
}  


응용 프로그램이 단일 창으로 이루어져 있으므로, 사용자가 창을 닫으면(기본 메뉴에서 종료 또는 나가기를 선택하는 대신) 응용 프로그램이 종료되도록 OnClosed 이벤트를 처리합니다.


명령줄 도구 설치


이전에 프로젝트 파일에서 참조했던 런타임 패키지 외에도 Electron.NET은 빌드 및 배포 작업을 수행하기 위한 명령줄 도구를 제공합니다.


VS Code에서 새 터미널 창 및 유형을 만듭니다.

dotnet tool install ElectronNET.CLI -g  


이 일회성 단계는 electronize라는 명령을 구현하는 .NET Core 전역 도구를 설치합니다. 시스템에 설치된 도구/명령 목록을 보려면 다음을 입력합니다.

dotnet tool list -g



Electronized 응용 프로그램 실행


명령줄 도구를 설치한 후 VS Code 터미널 창에서 다음 행을 입력합니다.

electronize init  
electronize start


첫 번째 행은 electron.manifest.json이라는 매니페스트 파일을 만들고 프로젝트에 이 파일을 추가하는 일회성 단계입니다.


두 번째 행은 Electron 응용 프로그램을 실행하는 데 사용됩니다. 브라우저에서 Blazor 응용 프로그램만 열기 때문에 F5 키를 사용하지 마십시오.


이제 브라우저가 아닌 응용 프로그램 창에 콘텐츠가 표시됩니다.

Blazor 응용 프로그램


기본 Electron 응용 프로그램 메뉴를 살펴봅니다. Mac의 경우 이 메뉴가 창의 일부가 아니고 화면 상단에 고정되어 있습니다.

Electron dot NET 응용 프로그램


Blazor 코드 디버깅


F5 키가 아닌 외부 명령을 사용하여 응용 프로그램을 실행했으므로 실행 중인 Blazor 프로세스에 디버거를 연결해야 합니다.


응용 프로그램 창이 열린 상태에서 VS Code로 이동한 후 Pages/Counter.razor를 열고 14행에서 중단점을 설정합니다.


작업 막대에서 실행을 클릭하고 드롭다운 컨트롤에서 .NET Core 연결을 선택한 다음 옆에 있는 아이콘을 클릭하여 프로세스 목록이 나타나게 합니다.


응용 프로그램 이름(BlazorApp)을 목록에 입력한 후 남아 있는 한 가지 항목을 선택합니다. (여러 프로세스가 계속 표시되면 electronWebPort 값이 가장 큰 프로세스를 선택합니다.)

VS Code에서 디버깅을 위해 프로세스에 연결


응용 프로그램 창에서 카운터 페이지를 방문하고 버튼을 클릭하여 중단점을 트리거합니다.


실행을 계속하고 응용 프로그램 창을 닫은 후 디버거가 자동으로 연결 해제되는지 확인합니다.



샘플 데이터 페이지 사용자 정의


Electron.NET의 크로스 플랫폼 기능을 보여주기 위해 기본 날씨 데이터 페이지를 활성 시스템 프로세스 목록으로 바꿉니다. 나중에 Linux 버전을 빌드하고 해당 플랫폼에서는 어떤 차이가 있는지 확인합니다.


먼저, Data 폴더에서 ProcessService.cs라는 새 파일을 만들고 다음 코드를 입력합니다.

using System;  
using System.Linq;  
using System.Threading.Tasks;  
using System.Diagnostics;

namespace BlazorApp.Data  
{  
    public class ProcessService  
    {  
        public Task<Process[]> GetProcessesAsync()  
        {  
            return Task.FromResult(Process.GetProcesses().Where(p => !String.IsNullOrEmpty(p.ProcessName)).ToArray());  
        }  
    }  
}  


그다음, Startup.cs를 열고 ConfigureServices 메서드 끝에 다음 행을 추가하여 서비스를 등록합니다.

services.AddSingleton<ProcessService>();  


Pages 폴더에서 Processes.razor라는 새 파일을 만들고 다음 코드를 붙여넣습니다.

@page "/processes"

@using BlazorApp.Data  
@using System.Diagnostics  
@inject ProcessService ProcessService

<h1>Processes</h1>

<p>This component displays a list of system processes.</p>

@if (processes == null)  
{  
    <p><em>Loading...</em></p>  
}  
else  
{  
    <table class="table">  
        <thead>  
            <tr>  
                <th>Id</th>  
                <th>Process Name</th>  
                <th>Physical Memory</th>  
            </tr>  
        </thead>  
        <tbody>  
            @foreach (var process in processes)  
            {  
                <tr>  
                    <td>@process.Id</td>  
                    <td>@FormatName(process)</td>  
                    <td>@process.WorkingSet64</td>  
                </tr>  
            }  
        </tbody>  
    </table>  
}

@code {  
    private System.Diagnostics.Process[] processes;

    protected override async Task OnInitializedAsync()  
    {  
        processes = await ProcessService.GetProcessesAsync();  
    }

    private string FormatName(System.Diagnostics.Process process)  
    {  
        string name;  
        const int maxLength = 30;  
        try  
        {  
            name = process.MainModule.ModuleName;  
        }  
        catch  
        {  
            name = process.ProcessName;  
        }  
        if (name.Length > maxLength)  
        {  
            name = name.Substring(0, maxLength) + "...";  
        }  
        return name;  
    }  
} 


이렇게 하면 ID 번호, 프로세스 이름, 프로세스에 할당된 실제 메모리 양에 해당하는 열이 있는 명명된 프로세스 표가 표시됩니다. FormatName 함수를 사용합니다.


일부 플랫폼에서는 프로세스가 잘릴 수 있으므로, 프로세스 이름이 대체 값으로 사용되는 모듈 이름이 선호됩니다. 어떤 경우든 결과 문자열은 30자에서 잘립니다.


마지막으로 Shared/NavMenu.razor 파일을 편집하고 마지막 목록 요소를 다음으로 바꿉니다.

<li class="nav-item px-3">  
    <NavLink class="nav-link" href="processes">  
        <span class="oi oi-cog" aria-hidden="true"></span> Processes  
    </NavLink>  
</li> 


Electron.NET은 변경 내용을 모니터링하고 응용 프로그램을 자동으로 다시 빌드 및 다시 실행하는 관찰 모드를 지원합니다. 관찰 모드를 호출하려면 다음 명령을 실행합니다.

electronize start /watch  


이제 모든 변경 내용을 프로젝트에 저장합니다. 응용 프로그램이 다시 시작된 후 왼쪽에 있는 프로세스 링크를 클릭하면 다음과 유사하게 표시됩니다.

프로세스를 실행 중인 Blazor 응용 프로그램


세부 정보 보기 추가


일반적인 CRUD 응용 프로그램에서 목록의 항목에는 사용자가 항목을 더 자세히 보거나 해당할 경우 수정하는 세부 페이지로의 이동 링크가 포함되어 있습니다.


개별 프로세스에 대한 간단한 보기를 만들어 보겠습니다.


먼저 Pages 폴더에 Process.razor라는 새 파일을 추가한 후 다음 마크업을 삽입합니다.

@page "/process/{Id:int}" 

@using BlazorApp.Data  
@using ElectronNET.API  
@using ElectronNET.API.Entities  
@inject ProcessService ProcessService  
@inject NavigationManager UriHelper

<h1>Process view</h1>

<p>This component displays details for a single system process.</p>

@if (process == null)  
{  
    <p><em>Loading...</em></p>  
}  
else  
{  
    <div>  
        <dl class="row">  
            @foreach (var property in @PropertyList.Select(name => typeof(System.Diagnostics.Process).GetProperty(name)))  
            {  
            <dt class="col-sm-4">  
                @property.Name  
            </dt>  
            <dd class="col-sm-8">  
                @property.GetValue(process)  
            </dd>  
            }  
        </dl>  
    </div>  
    <div>  
        <hr />  
        <button class="btn btn-danger" @onclick="@(() => Delete())">Kill Process</button>  
        <a class="btn btn-light" href="processes">Back to List</a>  
    </div>  
}

@code {  
    private System.Diagnostics.Process process;  
    private static readonly string[] PropertyList = new[]  
    {  
        "Id", "ProcessName", "PriorityClass", "WorkingSet64"  
    };

    [Parameter]  
    public int Id { get; set; }

    protected override void OnParametersSet()  
    {  
        process = System.Diagnostics.Process.GetProcessById(Id);  
    }

    private async Task Delete()  
    {  
        await Task.Run(() =>  
        {  
            process.Kill();  
            UriHelper.NavigateTo("processes");  
        });  
    }  
} 


첫 번째 행은 프로세스 ID 번호를 나타내는 정수 매개 변수를 사용하여 세부 페이지에 대한 라우팅을 지정합니다.


PropertyList 문자열 배열은 세부 정보 보기에 표시될 Process 개체 속성의 목록을 정의합니다. 페이지 마크업에서 해당 문자열을 하드코딩하는 대신, 여기서는 리플렉션을 사용하여 속성 이름 및 값을 런타임으로 파생합니다.


세부 정보 보기를 프로세스 페이지의 개별 항목에 링크하기 위해 Pages/Processes.razor를 편집하고

@process.Id 


위의 식을 다음 앵커 태그로 바꿉니다.

<a href="process/@process.Id">@process.Id</a> 


응용 프로그램을 실행합니다. Id 열에 다음과 유사한 페이지로 이동하는 하이퍼링크가 포함되어 있는지 확인합니다.

Blazor 응용 프로그램 단일 프로세스


프로세스 종료 버튼의 onclick 처리기가 사용자에게 다시 한번 생각하고 작업을 취소할 기회를 제공하지 않고 Kill 메서드를 무조건 호출한다는 점을 알아챘을 수 있습니다.


Electron.NET의 ShowMessageBoxAsync API를 사용하여 사용자에게 플랫폼 관련 확인 대화 상자를 표시하도록 Delete 메서드를 다시 작성해 보겠습니다.

private async Task Delete()  
{  
    const string msg = "Are you sure you want to kill this process?";  
    MessageBoxOptions options = new MessageBoxOptions(msg);  
    options.Type = MessageBoxType.question;  
    options.Buttons = new string[] {"No", "Yes"};  
    options.DefaultId = 1;  
    options.CancelId = 0;  
    MessageBoxResult result = await Electron.Dialog.ShowMessageBoxAsync(options);

    if (result.Response == 1)  
    {  
        await Task.Run(() =>  
        {  
            process.Kill();  
            UriHelper.NavigateTo("processes");  
        });  
    }  
}


이렇게 사용자가 취소하는 경우 세부 페이지는 현재 상태를 유지합니다. 그러지 않으면 프로세스를 종료한 후 응용 프로그램이 프로세스 페이지로 리디렉션합니다.

Electron dot NET 응용 프로그램


서트-파티, ComponentOne(C1) 컨트롤 추가


Blazor 프로젝트와 마찬가지로 타사 컨트롤을 Electron.NET 응용 프로그램에 추가할 수 있습니다.


카운터 페이지를 ComponentOne 달력 컨트롤로 바꿔보겠습니다. 먼저 다음 패키지 참조를 .csproj 파일에 추가합니다.

<PackageReference Include="C1.Blazor.Calendar" Version="3.1.20203.*" />


_Pages/Host.cshtml을 편집하고 닫는 태그 앞에 다음 스타일시트 참조를 삽입합니다.

<link rel="stylesheet" href="/_content/C1.Blazor.Core/styles.css" />  
<link rel="stylesheet" href="/_content/C1.Blazor.Calendar/styles.css" />  


또한 같은 파일에서 닫는 태그 앞에 다음 스크립트 참조를 삽입합니다.

<script src="/_content/C1.Blazor.Core/scripts.js"></script>  
<script src="/_content/C1.Blazor.Calendar/scripts.js"></script>


Pages 폴더에서 Calendar.razor라는 새 파일을 만들고 다음 코드를 붙여넣습니다.

@page "/calendar"

@using C1.Blazor.Calendar

<h1>Calendar</h1>

<C1Calendar></C1Calendar>


마지막으로 Shared/NavMenu.razor 파일을 편집하고 두 번째 목록 요소를 다음으로 바꿉니다.

<li class="nav-item px-3">  
    <NavLink class="nav-link" href="calendar">  
        <span class="oi oi-calendar" aria-hidden="true"></span> Calendar  
    </NavLink>  
</li>


모든 변경 내용을 저장합니다. 빌드 프로세스는 ComponentOne Studio Enterprise 30일 평가판 라이선스를 생성합니다. 왼쪽에서 달력 링크를 클릭하면 다음과 유사한 화면이 표시되어야 합니다.

Blazor 달력 컨트롤


다른 플랫폼용 빌드


다른 플랫폼용 설치 미디어를 빌드하려면 터미널 창에서 다음 명령을 실행합니다.

electronize build /target xxx /PublishReadyToRun false 


여기서 xxx는 win, linux 또는 osx 중 하나입니다. 출력은 bin/Desktop 폴더로 이동합니다. 예:

  • BlazorApp Setup 1.0.0.exe(windows)

  • BlazorApp-1.0.0.AppImage(linux)

  • BlazorApp-1.0.0.dmg(osx)


Windows 실행 파일은 응용 프로그램 자체가 아닌 설치 프로그램입니다.


OSX 대상은 Mac에서만 빌드할 수 있지만 Windows/Linux 대상은 어느 플랫폼에서나 빌드할 수 있습니다. 버전 번호, 저작권 고지 및 기타 특성을 변경하려면 설치 미디어를 빌드하기 전에 electron.manifest.json을 편집합니다.


Linux에서 실행되는 응용 프로그램이 다음과 같이 표시됩니다.

Blazor 응용 프로그램 Linux


결론 및 샘플 코드


Electron.NET은 C# 개발자에게 Windows, Linux 및 macOS용 크로스 플랫폼 데스크톱 응용 프로그램을 전달하기 위한 수단을 제공하여 Blazor에 가치를 더하는 오픈 소스 도구입니다.


ComponentOne Studio Enterprise의 Blazor 컨트롤과 같은 타사 컴포넌트와도 호환됩니다.


이 문서에서 설명한 완성된 프로젝트의 소스 코드는 GitHub(https://github.com/jjuback/BlazorApp)에서 제공됩니다.



  • 페이스북으로 공유
  • 트위터로  공유
  • 구글플러스로 공유
  • 카카오톡으로 보내기

댓글목록

등록된 댓글이 없습니다.

그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기

태그

인기글

더보기
  • 인기 게시물이 없습니다.
그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기
이메일 : sales-kor@grapecity.com | 전화 : 1670-0583 | 경기도 안양시 동안구 시민대로 230, B-703(관양동, 아크로타워) 그레이프시티(주) 대표자 : 허경명 | 사업자등록번호 : 123-84-00981 | 통신판매업신고번호 : 2013-경기안양-00331 Copyright ⓒ 2021 GrapeCity inc.