Blazor
๋จ์ผ ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ์์ ์๋ฒ์ชฝ ๋ ๋๋ง ๋ฐ ํด๋ผ์ด์ธํธ ๋ํํ ์์
์ ๋ชจ๋ ์ง์ํ๋ .NET ์น ํ๋์์ํฌ
- Razor ํด๋์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ NuGet ํจํค์ง๋ก ๊ณต์ ๋ฐ ๋ฐฐํฌ๋ ์ ์์ต๋๋ค.
- ํด๋์ค๋ ์ผ๋ฐ์ ์ผ๋ก
.razor
ํ์ผ ํ์ฅ์๋ฅผ ๊ฐ์ง Razor ํ๊ทธ ํ์ด์ง ํ์์ผ๋ก ์์ฑ
- Razor๋ HTML, CSS, C# ์ฝ๋๋ฅผ ๊ฒฐํฉํ ๊ตฌ๋ฌธ
<PageTitle>Counter</PageTitle> <h1>Counter</h1> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
Blazor Web Apps
๋จ์ผ ์๋ฃจ์
์์ ์๋ฒ์ชฝ ๋ ๋๋ง ๋ฐ ์ ์ฒด ํด๋ผ์ด์ธํธ์ชฝ ๋ํํ ์์
์ ํฌํจํ๋ ๊ตฌ์ฑ ์์ ๊ธฐ๋ฐ ์ํคํ
์ฒ๋ฅผ ์ ๊ณตํ๋ฉฐ, ์๋ฒ์ชฝ ํด๋ผ์ด์ธํธ์ชฝ ๋ ๋๋ง ๋ชจ๋ ๊ฐ์ ์ ํํ๊ณ ๋์ผํ ํ์ด์ง์์ ํผํฉํ ์๋ ์์
- ์๋ฒ์์ UI ๋ ๋๋ง์ด ๋น ๋ฅด๊ฒ ์ํ๋๋ฏ๋ก ํ์ด์ง๊ฐ ๋น ๋ฅด๊ฒ ๋ก๋๋จ
- ๋ธ๋ผ์ฐ์ ์์ ์ค์๊ฐ ์ฐ๊ฒฐ์ ํตํด ์๋ฒ์์ UI ์ํธ ์์ฉ์ ์ฒ๋ฆฌํ๋ ๋ํํ SSR์ ์ง์
๋ํํ SSR(Interactive SSR)
์ ์ฌ์ฉํ๋ฉด ํด๋ผ์ด์ธํธ ์ฑ์์ ๊ธฐ๋ํ๋ ๊ฒ์ฒ๋ผ ํ๋ถํ ์ฌ์ฉ์ ํ๊ฒฝ์ ์ฌ์ฉํ ์ ์์ง๋ง ์๋ฒ ๋ฆฌ์์ค์ ์ก์ธ์คํ๊ธฐ ์ํด API ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค ํ์๊ฐ ์๋ค. ๋ํํ ํ์ด์ง์ ํ์ด์ง ์ฝํ ์ธ ๋ ๋ฏธ๋ฆฌ ๋ ๋๋ง๋จ
Blazor Hybrid
์น, ๋ชจ๋ฐ์ผ ๋ฐ ๋ฐ์คํฌํฑ ํ๋ซํผ์ ๋ํ ๋ค์ดํฐ๋ธ ๋ฐ ์น ๊ธฐ์ ์ด ํผํฉ๋ ๋ค์ดํฐ๋ธ ํด๋ผ์ด์ธํธ ์ฑ
- .NET ํ๋ก์ธ์ค์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์คํ
- ๋ก์ปฌ interop ์ฑ๋์ ์ฌ์ฉํ์ฌ ํฌํจ๋ Web View ์ปจํธ๋กค์ ์น UI๋ฅผ ๋ ๋๋ง
- C# ๋ฐ XAML์ ์ฌ์ฉํ์ฌ ๋ค์ดํฐ๋ธ ๋ชจ๋ฐ์ผ ๋ฐ ๋ฐ์คํฌํฑ ์ฑ์ ๋ง๋ค๊ธฐ ์ํ ํ๋ซํผ ๊ฐ ํ๋ ์์ํฌ์ธ .NET MAUI๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋๋จ
Blazor Project Contents
Program.cs
์๋ฒ๋ฅผ ์์ํ๊ณ ์ฑ ์๋น์ค์ ๋ฏธ๋ค์จ์ด๋ฅผ ๊ตฌ์ฑํ๋ ์ฑ์ ์ง์
์
Components
- App.razor : ์ฑ์ ๋ฃจํธ๋ฅผ ๊ตฌ์ฑํ๋ ์์
- Routes.razor : Blzor ๋ผ์ฐํฐ๋ฅผ ๊ตฌ์ฑ
- Pages : ๋๋ ํฐ๋ฆฌ์๋ ์ฑ์ ๋ํ ๋ช ๊ฐ์ง ์์ ์น ํ์ด์ง๊ฐ ํฌํจ
Propertiesย ๋๋ ํฐ๋ฆฌ ๋ด์ย launchSettings.json
ย ํ์ผ
๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ํ ๋ค์ํ ํ๋กํ ์ค์ ์ ์ ์
ํฌํธ ๋ฒํธ๋ ํ๋ก์ ํธ ์์ฑ ์ ์๋์ผ๋ก ํ ๋น๋์ด ์ด ํ์ผ์ ์ ์ฅ
๋ํํ ์์ ์ฌ์ฉ
๊ตฌ์ฑ ์์์์ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋ ค๋ฉด ๊ตฌ์ฑ ์์๊ฐ ๋ํํ(SSR)์ด์ด์ผํจ
- ์๋ฒ์์ ์ ์ ์ผ๋ก ๋ ๋๋ง
- ์ง์๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ํํ ๋ ๋๋ง ๋ชจ๋๋ฅผ ์ ์ฉํ์ฌ ๊ตฌ์ฑ ์์๋ฅผ ๋ํํ์ผ๋ก ๋ง๋ฌ
@rendermode
@rendermode InteractiveServer <Counter @rendermode="InteractiveServer" />
๋ํํ ์๋ฒ ๋ ๋๋ง์ ๋ธ๋ผ์ฐ์ ์์ WebSocket ์ฐ๊ฒฐ์ ํตํด ์๋ฒ์ UI ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌ
- Blazor๋ ์ฑ์ ๊ตฌ์ฑ ์์์์ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ฐ๊ฒฐ์ ํตํด ์๋ฒ์ UI ์ด๋ฒคํธ๋ฅผ ๋ณด๋
- Blazor๋ ๋ ๋๋ง๋ ์ ๋ฐ์ดํธ๋ฅผ ์ฌ์ฉํ์ฌ ๋ธ๋ผ์ฐ์ DOM ์ ๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌ
๋๋
- ๋ ๋๋ง ๋ชจ๋๋ฅผ
InteractiveWebAssembly
์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ์์ ๋ํํ์ผ๋ก ๋ ๋๋ง
- ๊ตฌ์ฑ ์์ ์ฝ๋๊ฐ ๋ค์ด๋ก๋๋๊ณ WebAssembly ๊ธฐ๋ฐ .NET ๋ฐํ์์ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ ์ชฝ ์คํ
ํธ์คํ ๋ชจ๋ธ
Blazor Server
ASP.NET Core ์ฑ ๋ด์ ์น ์๋ฒ์์ ์คํ
ํด๋ผ์ด์ธํธ ์ชฝ์ UI์
๋ฐ์ดํธ, ์ด๋ฒคํธ ๋ฐ JavaScript ํธ์ถ์ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์
SignalR
์ฐ๊ฒฐ์ ํตํด ์ ์ก (์ค์๊ฐ์ ์ ์ฉ)SignalR ๊ธฐ๋ฅ
- ์ฐ๊ฒฐ ๊ด๋ฆฌ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌ
- ๋ชจ๋ ์ฐ๊ฒฐ๋ ํด๋ผ์ด์ธํธ์ ๋์์ ๋ฉ์์ง ์ ์ก
- ํน์ ํด๋ผ์ด์ธํธ๋ ํด๋ผ์ด์ธํธ ๊ทธ๋ฃน์ผ๋ก ๋ฉ์์ง๋ฅผ ์ ์ก
- ๋์ด๋ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๋๋ก ํฌ๊ธฐ๋ฅผ ์กฐ์
Blazor WebAssembly
Blazor ์ฑ, ํด๋น ์ข
์์ฑ ๋ฐ .NET ๋ฐํ์์ด ๋ค์ด๋ก๋ ๋์ด ๋ธ๋ผ์ฐ์ ์์ ์คํ
.NET MAUI ํ๋ก์ ํธ ๊ตฌ์กฐ ๋ฐ ์์์ด ํฌํจ๋ Blazor ํ์ด๋ธ๋ฆฌ๋
Blazor ํ๋ก์ ํธ ํ์ผ
Pages
: Blazor ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌ์ฑํ๋ ์ธ ํ์ด์ง๋ฅผ ์ ์
Shared
: ๊ธฐ๋ณธ ๋ ์ด์์ ๋ฐ ํ์ ๋ฉ๋ด๋ฅผ ๋น๋กฏํ ๊ณต์ Razor ๊ตฌ์ฑ ์์๊ฐ ํฌํจ
wwwroot
: HTML,CSS,JavaScript ๋ฐ ์ด๋ฏธ์ง ํ์ผ์ ํฌํจํ์ฌ Blazor์์ ์ฌ์ฉํ๋ ์ ์ ์น ์์ฐ์ด ํฌํจ
Main.razor
: ์น ๋ณด๊ธฐ ๋ด์์ ํ์ด์ง ํ์์ ์ฒ๋ฆฌํ๋๋ก Blazor ๋ผ์ฐํฐ๋ฅผ ์ค์ ํ๋ ์ฑ์ ๋ฃจํธ Razor ๊ตฌ์ฑ ์์
_Imports.razor
: ๊ฐ Razor ๊ตฌ์ฑ ์์๋ก ๊ฐ์ ธ์ค๋ ๋ค์์คํ์ด์ค๋ฅผ ์ ์
.NET MAUI ํ๋ก์ ํธ ํ์ผ
App.xaml
- ์ฑ์ด XAML ๋ ์ด์์์์ ์ฌ์ฉํ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ฆฌ์์ค๋ฅผ ์ ์
- ๊ธฐ๋ณธ ๋ฆฌ์์ค๋
Resources
ํด๋์ ์์ผ๋ฉฐ ์ฑ ์ ์ฒด ์๊ณผ .NET MAUI์ ๋ชจ๋ ๊ธฐ๋ณธ ์ ๊ณต ์ปจํธ๋กค์ ๊ธฐ๋ณธ ์คํ์ผ์ ์ ์
App.xaml.cs
- App.xaml ํ์ผ์ ์ฝ๋ ์จ๊น
- ์ฑ ํด๋์ค๋ฅผ ์ ์
- ๋ฐํ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํ๋
- ํด๋์ค์ ์์ฑ์๋ ์ด๊ธฐ ์ฐฝ์ ๋ง๋ค๊ณ
MainPage
์์ฑ์ ํ ๋น - ์ด ์์ฑ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์คํ๋ ๋ ํ์๋๋ ํ์ด์ง๋ฅผ ๊ฒฐ์
- ์ผ๋ฐ์ ์ธ ํ๋ซํผ ์ค๋ฆฝ ์ ํ๋ฆฌ์ผ์ด์
์๋ช
์ฃผ๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ ์ํ ์ ์์ (
OnStart
,OnResume
,Onsleep
)
MainPage.xaml
- ์ฌ์ฉ์ ์ธํฐํ์ด์ค ์ ์๊ฐ ํฌํจ
- .NET MAUI Blazor ์ฑ ํ
ํ๋ฆฟ์์ ์์ฑํ๋ ์ํ ์ฑ์ CSS ์ ํ๊ธฐ(
#app
)์์ ์ง์ ํ ์์น์ ์ง์ ๋ ํธ์คํธ HTML ํ์ด์ง(wwwroot/index.html
)์Main
๊ตฌ์ฑ ์์๋ฅผ ๋ก๋ํ๋BlazorWebView
๋ก ๊ตฌ์ฑ
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:BlazorHybridApp" x:Class="BlazorHybridApp.MainPage" BackgroundColor="{DynamicResource PageBackgroundColor}"> <BlazorWebView HostPage="wwwroot/index.html"> <BlazorWebView.RootComponents> <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" /> </BlazorWebView.RootComponents> </BlazorWebView> </ContentPage>
- MainPage.xaml.cs
- ํ์ด์ง์ ์ฝ๋ ์จ๊น
- ํ์ด์ง ํธ๋ฆฌ๊ฑฐ์์ .NET MAUI๊ฐ ์ ์ดํ๋ ๋ค์ํ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ธฐ ๋ฐ ๊ธฐํ ์์ ์ ๋ํ ๋ ผ๋ฆฌ๋ฅผ ์ ์
namespace BlazorHybridApp; public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } }
MauiProgram.cs
- ๋ค์ดํฐ๋ธ ํ๋ซํผ๋ง๋ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๊ณ ์ด๊ธฐํํ๋ ๋ค๋ฅธ ์์์
- ํ๋ก์ ํธ์ Platforms ํด๋์ ์์
- ์ ์
MauiProgram
ํด๋์ค์CreateMauiApp
๋ฉ์๋๋ฅผ ํธ์ถ CreateMauiApp
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ ์์ฑ๊ธฐ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ฑ- ์ฑ ์์ฑ๊ธฐ ๊ฐ์ฒด์
UseMauiApp
์ ๋ค๋ฆญ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์์ ์ ์ํํ ์ ์์ผ๋ฉฐ, ํ์ ๋งค๊ฐ ๋ณ์๋ ์ ํ๋ฆฌ์ผ์ด์ ํด๋์ค๋ฅผ ์ง์ - ๊ธ๊ผด ๋ฑ๋ก, ์ข ์์ฑ ์ฃผ์ ์ ์ฌ์ฉ๋๋ ์๋น์ค ๊ตฌ์ฑ, ์ปจํธ๋กค์ ์ฌ์ฉ์ ์ง์ ์ฒ๋ฆฌ๊ธฐ ๋ฑ๋ก ๋ฑ๊ณผ ๊ฐ์ ์์ ์ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ฅผ ์ ๊ณต
- ์ฑ ์์ฑ๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธ๊ผด์ ๋ฑ๋กํ๊ณ , ๋ ์จ ์๋น์ค๋ฅผ ๋ฑ๋กํ๊ณ ,
AddMauiBlazorWebView
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ Blazor ํ์ด๋ธ๋ฆฌ๋์ ๋ํ ์ง์์ ์ถ๊ฐ
using Microsoft.AspNetCore.Components.WebView.Maui; using BlazorHybridApp.Data; namespace BlazorHybridApp; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); builder.Services.AddMauiBlazorWebView(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); builder.Logging.AddDebug(); #endif builder.Services.AddSingleton<WeatherForecastService>(); return builder.Build(); } }
HttpClient ํด๋์ค
- ์ฑ์ด REST ์น ์๋น์ค์ HTTP ์์ฒญ์ ๋ณด๋ด๊ณ HTTP ์๋ต์ ์์ ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ .NET ํด๋์ค
- URI ์งํฉ์ ์น ์๋น์ค๊ฐ ๋ ธ์ถํ๋ ๋ฆฌ์์ค๋ฅผ ์๋ณ
- URI๋ ์น ์๋น์ค์ ์ฃผ์๋ฅผ ํด๋น ์ฃผ์์์ ์ฌ์ฉํ ์ ์๋ ๋ฆฌ์์ค์ ์ด๋ฆ๊ณผ ๊ฒฐํฉ
using System.Net.Http; ... var client = new HttpClient();
HttpClient๋ก ์ ๋ฆฌ์์ค ๋ง๋ค๊ธฐ
HttpClient client = new HttpClient(); // ์น ์๋น์ค๋ก ์ ์ก๋๋ ์์ฒญ์ ๋ชจ๋ธ๋ง, HTTP ๋์ฌ, ์น ์๋น์ค์ URL์ ์ง์ HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url); // ์์ฑ์ ํตํด ๋ณด๋ผ ํ์ด๋ก๋๋ฅผ ์ฑ์ message.Content = JsonContent.Create<Part>(part); HttpResponseMessage response = await client.SendAsync(message);
- ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ์ฌ์ฉํ๋ client๋ผ๋
HttpClient
์ ์ธ์คํด์ค๋ฅผ ๋ง๋ฌ
- ๋ฉ์์ง๋ฅผ ๋ชจ๋ธ๋งํ๋ ๋ฐ ์ฌ์ฉํ๋ message๋ผ๋
HttpRequestMessage
์ ์ธ์คํฐ์ค๋ฅผ ๋ง๋ญ๋๋ค. message์๋ HTTP ๋์ฌ์ URL ์ด ์์
JsonContent.Create
ํจ์๋ฅผ ์ฌ์ฉํ์ฌHttpRequestMessage
์Content
์์ฑ์ ์ค์ , ์ด ํจ์๋ part๋ณ์๋ฅผ ์น ์๋น์ค๋ก ๋ณด๋ด๊ธฐ์ ์ ํฉํ JSON์ผ๋ก ์๋์ผ๋ก ์ง๋ ฌํ
HttpClient
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์์ง๋ฅผ ๋ณด๋ ๋๋ค.HttpResponseMessage
๋ ์ํ ์ฝ๋ ๋ฐ ์น ์๋น์ค์์ ๋ฐํํ๋ ์ ๋ณด์ ๊ฐ์ ์ ๋ณด๊ฐ ํฌํจ
HttpClient๋ก ๋ฆฌ์์ค ์ฝ๊ธฐ
HttpClient client = new HttpClient(); // GetStringAsync ๋ฉ์์ค๋ ๋ฆฌ์์ค๋ฅผ ์ฐธ์กฐํ๋ URI๋ฅผ ์ฌ์ฉํ๊ณ ์๋ต์ ๋ฌธ์์ด๋ก ๋ฐํ string text = await client.GetStringAsync("https://..."); //ใ ๋๋ฅผ ์ถ๊ฐํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํน์ ํ์์ผ๋ก ๋ฐํํด์ผ ํ๋ค๊ณ ์น ์๋น์ค์ ์๋ ค์ค client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
- ๋ฌธ์์ด๋ก ๋ฐํ๋๊ณ ์๋ต ๋ฉ์์ง ๋ณธ๋ฌธ๋ง ํฌํจ
- ํค๋, ๋ณธ๋ฌธ ๋ฐ ์ํ ์ฝ๋๋ฅผ ํฌํจํ ์ ์ฒด ์๋ต์ ๊ฐ์ ธ์ค๋ ค๋ฉด
GetAsync
๋ฉ์๋๋ฅผ ํธ์ถ
- ๋ฐ์ดํฐ๊ฐ
HttpResponseMessage
๊ฐ์ฒด๋ก ๋ฐํ๋จ
HttpClient๋ก ๋ฆฌ์์ค ์ ๋ฐ์ดํธ
HttpClient client = new HttpClient(); HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Put, url); message.Content = JsonContent.Create<Part>(part); HttpResponseMessage response = await client.SendAsync(message);
POST
์ PUT
์ฌ์ด์ ๊ทผ๋ณธ์ ์ธ ์ฐจ์ด๋ ๋ฉฑ๋ฑ์ฑ
๋์ผํ PUT
์์ฒญ์ ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณตํ๋ฉด ๋์ผํ ๋ฆฌ์์ค๊ฐ ๋์ผํ ๋ฐ์ดํฐ๋ก ์
๋ฐ์ดํธ๋๊ณ ์์ฒญ์ด ํ ๋ฒ๋ง ์ ์ก๋ ๊ฒฝ์ฐ์ ํจ๊ณผ๊ฐ ๋์ผ
๋์ผํ POST
์์ฒญ์ ์ฌ๋ฌ ๋ฒ ์คํํ๋ฉด REST ์๋น์ค๊ฐ ๋ฆฌ์์ค์ ์ฌ๋ฌ ๋ณต์ฌ๋ณธ์ ๋ง๋ค๊ฒ ๋จHttpClient๋ก ๋ฆฌ์์ค ์ญ์
HttpClient client = new HttpClient(); HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Delete, url); HttpResponseMessage response = await client.SendAsync(message);