Fast Blazor 적응기 - slog

Microsoft Fast의 Blazor 컴포넌트인 Fast Blazor에 적응하고 업무에 적용하기 위한 단기 슬로그 입니다.

9개의 좋아요

개인적으로 가장 마음에 드는 Blazor UI 라이브러리인 MudBlazor와 어떠한 차이가 있는지 어떤 장/단점이 있는지도 같이 비교할 예정입니다.

4개의 좋아요

오… 저도 toy project를 MudBlazor로 픽했는데ㅎㅎ… awesomeblazor에서 뒤진 결과 제일 낫다고 판단했습니다. 물론 다른 component들은 써보지 않아서 비교해주시면 도움이 많이 될 것 같습니다!

4개의 좋아요

Fast Blazor는 다음의 명령으로 프로젝트 템플릿으로 빠르게 시작할 수 있습니다.

| 프로젝트 템플릿 설치

dotnet new install Microsoft.Fast.Templates.FluentUI::2.0.3

이후 새 프로젝트에 관련 프로젝트 템플릿을 선택할 수 있게 됩니다.

image

image

3개의 좋아요

디자인 토큰

FAST는 적응형 UI(Adaptive UI)라는 시스템을 제공합니다. 이에 따라 즉각적인 테마 적용이 가능해집니다.

https://www.fast.design/ 의 Dynamic themes with Adaptive UI를 살펴주세요.
image

이는 "디자인 토큰"을 통해 가능합니다. Fast Balzor에서는 각 토큰이 서비스에 등록되어 있어서 [Inject] 특성을 이용해 가져다 쓸 수 있습니다.

image

다음처럼 구성 요소로 직접 사용할 수 도 있습니다.

image

image

<FluentDesignSystemProvider>를 이용해 app.razor에 일괄 적용할 수도 있습니다.

image

4개의 좋아요

Fast Blazor의 데모는 아래에서 확인할 수 있습니다.

https://aka.ms/fluentui-blazor

4개의 좋아요

Fast 및 Fast Blazor 는 별도의 레이아웃 시스템이 없는 것 같습니다. 저 같이 css가 약한 개발자의 입장에서는 아쉬운 부분입니다.

이에 반해 MudBlazor는 css를 안써도 화면 구성이 가능할 정도로 다양한 레이아웃 컴포넌트를 제공합니다.

5개의 좋아요

Blazor에서 잘 준비된 컴포넌트를 사용하는 수준에서는 Fast Blazor나 MudBlazor나 특별한 난이도의 차이는 없어 보입니다. (그 컴포넌트의 사용법에 얼마나 빨리 익숙해지냐 정도의 차이입니다.)

다만 Fast Blazor는 Fast의 래핑이고 Fast는 현대적인 방식으로 웹 구성 요소 단위로 훌륭하게 디자인 및 확장을 가능하게 하는 점이 장점인 것 같습니다. (확신할 수 없는 이유는 아직 Fast를 제대로 사용하지 않았으므로)

특정 기술을 빠르게 익히기 위해서는 치트 시트(컨닝 페이퍼)만큼 좋은 것이 없습니다.

2개의 좋아요

Fast의 강점은 테마를 어떻게 동적으로 적용할 수 있는지 살펴보면 알 수 있습니다.

Fast Blazor의 샘플 코드 중 테마 스위치 코드를 살펴보면

    public async void SwitchTheme()
    {
        await Task.Delay(50);

        if (_inDarkMode)
            baseLayerLuminance = StandardLuminance.DarkMode;
        else
            baseLayerLuminance = StandardLuminance.LightMode;

        await BaseLayerLuminance.SetValueFor(container, baseLayerLuminance.GetLuminanceValue());

        GlobalState.SetLuminance(baseLayerLuminance);

        await _jsModule!.InvokeVoidAsync("switchHighlightStyle", baseLayerLuminance == StandardLuminance.DarkMode);
    }

이런 코드로 동작하게 됩니다. 여기서 핵심은 한줄인데요,

await BaseLayerLuminance.SetValueFor(container, baseLayerLuminance.GetLuminanceValue());

디자인 토큰 중 BaseLayerLuminance 값을 변경함을 알 수 있습니다. Fast에서는 다크모드와 라이트 모드를 Luminance(휘도)로 조정할 수 있는 듯 합니다. 즉, Dark|Light 가 아니라 0.2f, 0.8f 등의 특정 값으로 줄 수 있습니다. 그리고 그 적용은 즉각적으로 빠르게 적용됩니다.

image

image

3개의 좋아요

Fast Blazor를 현업에 사용하기 위한 남은 저의 숙제는 레이아웃 시스템입니다. 가장 빠르게 해결하는 방법은 그냥 Bootstrap의 레이아웃 시스템을 그대로 이용하는 것입니다 만… Blazor에서 컴포넌트 형태로 레이아웃을 사용할 방법을 좀 더 리서치 해봐야겠습니다.

살펴볼 만한 예로, Fast Blazor의 데모 코드를 보면 Stack이라는 레이아웃 컴포넌트가 있는데 구현을 살펴보면 간결하고 재밌습니다. (Stack은 우리가 알고 있는 StackPanel의 그것과 유사합니다)

| stack.razor

@* Remember to replace the namespace below with your own project's namespace. *@
@namespace FluentUI.Demo.Shared

@inherits FluentComponentBase

<div class="@ClassValue" style="@StyleValue" @attributes="AdditionalAttributes">
    @ChildContent
</div>

HTML 입장에서는 레이아웃 블록은 단지 div 블록일 뿐입니다.

| Stack.razor.cs

...

    protected string? ClassValue => new CssBuilder(Class)
        .AddClass("stack-horizontal", () => Orientation == Orientation.Horizontal)
        .AddClass("stack-vertical", () => Orientation == Orientation.Vertical)
        .Build();

    protected string? StyleValue => new StyleBuilder()
        .AddStyle("align-items", GetHorizontalAlignment(), () => Orientation == Orientation.Vertical)
        .AddStyle("justify-content", GetVerticalAlignment(), () => Orientation == Orientation.Vertical)

        .AddStyle("justify-content", GetHorizontalAlignment(), () => Orientation == Orientation.Horizontal)
        .AddStyle("align-items", GetVerticalAlignment(), () => Orientation == Orientation.Horizontal)

        .AddStyle("column-gap", $"{HorizontalGap}px", () => HorizontalGap.HasValue)
        .AddStyle("row-gap", $"{VerticalGap}px", () => VerticalGap.HasValue)
        .AddStyle("width", Width, () => !string.IsNullOrEmpty(Width))
        .AddStyle("flex-wrap", "wrap", () => Wrap)

        .AddStyle(Style)
        .Build();
...

속성에 따라 적절한 Class와 적절한 Style을 생성하는 것을 볼 수 있습니다.

Fast Blazor에서 Lab 형태로 (NuGet 패키지에는 포함되지 않음) 레이아웃 컴포넌트를 Header, Footer, BodyContent, Layout, MainLayout, Spacer, Stack으로 제공하지만 MudBlazor의 그것에 비해서는 많이 빈약합니다. (이것은 Fast의 문제라기 보다는 저의 css 활용의 제한에 따른 단점으로 비춰지는 점입니다.)

4개의 좋아요

Fast 특징 정리

Fast는 이미 잘 디자인된 Fast 컴포넌트를 사용자 지정 HTML 요소로 사용하고 사용자가 이 기반으로 새로운 사용자 컴포넌트를 만들 수 있도록 해줍니다. 이는 새 HTML 태그를 정의하고, 표준 구성 요소 수명 주기에 연결하고, HTML 렌더링 및 CSS를 캡슈화하고, CSS를 매개 변수화 하고, 구성 요소를 스킨하는 등의 기능이 포함됩니다. 이러한 기능은 표준화를 통해 거의 모든 브라우저에서 정상 동작합니다.

또한 다양한 프레임워크와 잘 결합되며 Fast 홈페이지에서 소개하는 프레임워크 - Angular, ASP.NET, Aurelia, Blazor, Ember, React, Rollup, Svelte, Vite, Vue, Webpack등과 잘 결합됩니다.

또한 Fast는 다음의 기술 스택으로 구현되어 있는데

image

이 기반으로 새롭게 구현할 필요 없이 기 구현된 다양한 기능 단위 (상태 관리, 접근성, 키보드 탐색 및 확장성/컴포지션 모델 등)와 동적 스타일 동작, CSS 속성 관리, 알고리즘 색상, RTL, 고대비 등의 기능을 제공합니다.

@microsoft/fast-foundation를 기반으로 Google의 머티리얼 디자인이나 트위터의 부트 스트랩과 같은 구현할 수도 있습니다.

Fast Blazor 특징 정리

Fast Blazor는 Fast의 Balzor 래핑입니다. Fast의 사용자 지정 HTML은 Blazor의 컴포넌트로 래핑됩니다.

디자인 토큰을 제외한 Fast의 기능이 Fast Blazor에 노출되는지는 파악되지 않습니다. (데모에는 나타나지 않습니다.) Fast를 이용해서 만들어진 FluentUI 컴포넌트를 Blazor에서 쉽게 사용할 수 있도록 Blazor 컴포넌트로 래핑한 정도로 이해가 됩니다.

Blazor에서 좀 더 편리하게 사용할 수 있도록 Lab 수준으로 여러 기능을 추가하려 하는 것 같습니다. 레이아웃 시스템 뿥만 아니라 NavMenu라던가 뱃지, Highligther 등입니다.

5개의 좋아요

검색해 본 결과 WPF의 Grid, StackPanel 같은 Blazor용 레이아웃 전용 컴포넌트 라이브러리는 나타나지 않는 것 같습니다. 어찌보면 CSS가 이미 그 것을 훌륭하게 해결하기 때문이 아닐까 생각하는데요, 참고가 되는 아래의 글을 공유합니다.

4개의 좋아요

CSS 격리 오동작

왠일인지 CSS 격리가 동작하지 않습니다. 분명히 다음 처럼 CSS 격리를 설정했는데

| _Laout.cshtml

...
<link href="BlazorApp33.styles.css" rel="stylesheet" />
...

| Components.razor.css

fluent-card {
    --card-height: 400px;
    --card-width: 500px;
    padding: 20px;
    margin: 12px;
}

.class-override {
    height: 163px;
    width: 300px;
}

.state-override {
    --card-width: 350px;
    --card-height: 300px;
}

.contents {
    display: flex;
    flex-direction: column;
}

실행하면 다음 처럼 스타일이 적용되지 않습니다.

분석해보면 변환된 html 코드를 보았을 때,

<fluent-card neutral-palette-source="#CABA8C" _bl_90769844-e72f-40fc-b290-c337320e18c4>
...
</fluent-card>

그리고 스타일은
| https://localhost:7061/BlazorApp33.styles.css

fluent-card[b-63vyab3aqy] {
    --card-height: 400px;
    --card-width: 500px;
    padding: 20px;
    margin: 12px;
}
...

로 맞지를 않네요? 아마도 자체 시스템을 이용하는 듯 하며 그 시스템에 맞게 스타일을 변경해야 하는 것 같습니다.

4개의 좋아요

정리

Fast 및 Fast Blazor는 (특히 Flast Blazor는 Fast가 존속하는 한) Microsoft가 관리하고 있으므로 적용해 볼만한 것으로 보입니다. 특히 Fast는 GitHub에서 8k의 지지를 받고 있는 점도 인상적입니다.

Blazor에서 사용하는 Fast는 다른 Blazor 컴포넌트 라이브러리와 특별히 다른 느낌은 없었지만 아마도 Fast의 이해도가 낮아서 생기는 시각에 의한 것으로 생각됩니다. 어쨌든 Fast Blazor를 사용하는 입장에서는 여타 Blazor 컴포넌트를 사용하는 감각으로 거의 그대로 사용 가능하며 대부분의 궁금증은 다음의 데모를 통해 찾고자 하는 것을 발견할 수 있습니다.

https://brave-cliff-0c0c93310.azurestaticapps.net/

CSS 격리의 경우 정확한 원인은 찾지 못했으나 Blazor 자체의 문제와 연관이 있는 듯 해 보입니다. Fast Blazor의 컴포넌트는 별개의 CSS 격리를 사용해서 나만의 컴포넌트를 만들 때 Fast 컴포넌트의에 스타일을 적용할 수 없었는데 지금은 이부분이 중요하지는 않으므로 길게 확인 후 해결방법을 찾으면 덧글을 달도록 하겠습니다.

레이아웃 시스템은 Lab의 Stack을 사용해서 접근하고 좀 더 다양한 구성은 Stack의 코드를 참고해서 만들어나가야겠습니다. 대부분의 레이아웃은 Stack만으로도 가능할 것 같아 보였습니다. 다만 반응형도 고려해야 하므로 이것 역시 차차 나가며 실제 적용했을 때의 솔루션을 댓글로 달도록 하겠습니다.

5개의 좋아요

@dimohy 님의 Slog 덕분에 Fluent UI Blazor를 테스트해보고 있습니다.

간결하고 좋은 것 같습니다. web component도 알게되고 fluent ui가 blazor로 포팅된 부분도 살펴볼 수 있었습니다. design token은 쉽게 적응하기 어려워서 css를 핸들링 하는 방법을 테스트 중입니다.

tailwindcss와 fluent ui를 함께 사용하기 위해 다음과 같이 사용하였습니다.
굳이 tailwindcss를 추가적으로 사용하는 이유는 계속 사용하다 보니 손에 익어서 그렇습니다.

FluentCard는 간단한 컴포넌트라 Class를 통해 tailwindcss 적용이 쉬웠습니다.

문제는 FluentButton은 렌더링 된것을 보면 Child Element로 button 태그가 들어가 있어서
fluent-button 태그에 css를 적용하기 어려웠습니다.
그리고, css 격리와 비슷한 효과를 얻고자 하였습니다.

(중략…) 다음과 같이 처리하였습니다.

  1. tailwindcss 설정

  2. postcss 및 autoprefixer 설치

  3. postcss-import와 postcss-easy-import 설치

npm install -D postcss-import postcss-easy-import
  1. postcss.config.js 설정
module.exports = {
    plugins: [
        require('postcss-easy-import'),
        require('tailwindcss'),
        require('autoprefixer'),
    ]
}
  1. package.json 설정
  "scripts": {
    "build": "postcss ./Src/base.styles.css -o ./wwwroot/css/StyleSheet.css",
    "watch": "postcss ./Src/base.styles.css -o ./wwwroot/css/StyleSheet.css --watch --verbose"
  }
  1. base.styles.css 파일 생성
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

@import './tag.styles.css';

/* postcss easy import */
@import '../Pages/**/*.razor.css';
  1. tag.styles.css 파일 생성
fluent-card {
    @apply border rounded-none shadow-none;
}

fluent-button {
    @apply border-0 rounded-none shadow-none;
}
  1. /Pages/Index.razor
<FluentCard Class="w-[300px] h-[300px] text-red-500">
  <h2>Hello World!</h2>
  <FluentButton id="test">Click Me</FluentButton>
</FluentCard>
  1. /Pages/Index.razor.css
#test::part(control) {
    @apply border text-red-500 bg-white text-[20px] hover:text-white hover:bg-red-500;
    --neutral-fill-hover: initial;
}
  1. wwwroot/css/StyleSheet.css 파일 생성
npm run build

일부만 테스트해 보았습니다.
내용을 올리는중 드는 생각은 굳이 이렇게까지 해서 사용해야 되나 싶습니다.

5개의 좋아요

아닙니다. 훌륭하네요… 관련 경험을 블로그 글로 공유 주시면 도움이 많이 될 듯 합니다.

3개의 좋아요

요컨대 익숙해지기 편한 기본 기능만 제공하고,
예쁘게 만드는 건 결국 css로 잡아줘야 하는거군요?

음, 오히려 그게 더 깔끔하게 보일 수도 있겠네요.
정 안되면 예쁜 css 구해다 붙여버려도 되고,
단… @_jeonghwan 님께서 정리해주신 것과 같은 문제가 있겠네요.

2개의 좋아요

CSS에서 flex와 grid가 어느정도 이숙해지니 <Stack> 등의 레이아웃 관련 컴포넌트가 거추장해졌습니다. class 또는 style이 아닌 별도의 컴포넌트 태그를 사용해야 해서 구조가 좀 더 복잡해지는 점도 있습니다. 이 정도 되니 HTML-CSS 사상 – 문서와 스타일을 구별하는 접근법이 XAML 구조 보다 좋아보이는 점도 생깁니다. (레이아웃을 빠르게 태그로 잡고 스타일을 먹여가는 재미도 좀 차이가 납니다)

4개의 좋아요

제가 세팅을 잘 못 해서 그런지는 몰라도,
닷넷 8.0에서 새로 바뀐 blazor의 기본 template과 호환이 되지 않는 것 같아요.

심지어 같은 런타임 조건 하에서, fast 전용, 즉 FluentUI 템플릿으로 생성하면 잘 작동 합니다만,
8 버전이 아니었을 때의 템플릿으로 생성해서 그런가 봐요.

2개의 좋아요

제가 테스트 했을 때는 잘 되었는데요, 도움을 드릴 수 있도록 프로젝트를 공유 가능 할까요?

3개의 좋아요