Fast Blazor 적응기 - slog

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

3개의 좋아요

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

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

2개의 좋아요

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

4개의 좋아요

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

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

2개의 좋아요

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

3개의 좋아요

확실히 8.0의 기본 Blazor 템플릿으로는 정상 작동이 되질 않네요.
슬프게도, 기본 css 조차도 먹질 않아요.
css 격리 덕분인지, Bootstrap 스타일도 먹통!
image

원래 기존 Blazor Server Template은,
Package 추가만 해도 버튼이 작동 하거든요.
(js import 조차 하지 않아도 가능합니다. 이전 버전 패키지에서 업그레이드 할 경우 js 배달 옵션을 끄라는 권장이 있어요.)
image

아무래도 Blazor App 자체의 구조가 살짝 바뀌어서 생긴 원인이 아닐까 싶은데요,
기존 Blazor Server의 경우
진입점인 Program.cs에서,

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// 이하생략...

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

여기를 통해
_Host.cshtml → App.razor 인스턴스 활성화 → RouteView(인증이 있으면 AuthorizedRouteView가 되겠죠?) →
MainLayout.razor → [RenderFragment?] 타입인 @body 로 메인 페이지를 처음 가져오는데요.

8.0와서 기본 구조가 바뀌었더라구요.

아예 처음의 Program.cs

builder.Services.AddRazorComponents()
    .AddServerComponents();

...

app.MapRazorComponents<App>()
    .AddServerRenderMode();

이미 여기서부터 모양이 살짝 달라졌지요?
근데 이제 호출 따라가보면
App.razor 부터 내용이 확 달라집니다.

//- 8.0의 App.razor
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="BlazorApp1.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
</head>

<body>
    <Routes />  // <- 여길 잘 봐주세요!
    <script src="_framework/blazor.web.js"></script>
</body>

</html>

///- 기존의 App.razor
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

분명 다른데 익숙한 이 느낌은,

사실 8.0의 저 구조는
_Host.cshtml에 있는 내용과 매우 흡사합니다.

//_Host.cshtml
@page "/"
@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp2.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorApp2.styles.css" rel="stylesheet" />
    <link rel="icon" type="image/png" href="favicon.png"/>
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    <component type="typeof(App)" render-mode="ServerPrerendered" />

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

헌데 8.0 템플릿에는 Route.razor가 있지요.
막상 기존 App.razor의 내용이 여기 들어가있습니다.

//Route.razor
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>

심지어, MainLayout에 @body가 있네요.

///MainLayout.razor
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

<div id="blazor-error-ui">
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

그렇다면,

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

Program.cs->_Host.cshtml → App.razor 인스턴스 활성화 → RouteView(인증이 있으면 AuthorizedRouteView가 되겠죠?) →
MainLayout.razor → @body 구조에서

app.MapRazorComponents<App>()
    .AddServerRenderMode();

Program.cs → App.razor → Routes.razor → MainLayout.razor → @body
로 바뀐걸로 정리 할 수 있겠습니다…만

여기서 제가 뭐가 문제인지 잘 모르겠더라고요.

여기서부터 제가 해본 시도인데,

혹시나 해서, 8.0의 Header 태그를 생성해주는 App.razor에다가

<script type="module" src="https://cdn.jsdelivr.net/npm/@fluentui/web-components/dist/web-components.min.js"></script>

을 추가했더니, 그제서야 비주얼적인 부분만 작동하더라고요. Hover 효과 등등, css부분만요!
심지어 eventHandler를 붙여도 먹통인 증상이 ㅜㅜ

혹시 ServerRender와 관련이 있는 건가?

라고 생각 했는데, 8.0부터(인 지는 모르겠습니다만…)는 prerendered가 기본값이더군요.

@attribute [RenderModeAuto(prerender:true)]
4개의 좋아요

그래서 혹시나 싶어서
Fast.UI 에서 제공하는 기본 생성 템플릿을 써봤습니다.
image
사실, Server App이라는 단어 자체가 8.0에서 변했어요.
서버 앱 에서 웹앱 선택 후 “선택형 서버 기능 추가” 선택으로 바뀌었거든요.
그러니까, 이미 템플릿 이름 부터 예전 버전 기반임을 알 수 있습니다.

솔루션 파일 구조 보시면 Shared도 있고, _Host.cshtml이 있는걸 보면
기존 템플릿에 닷넷 버전만 올린 모양이라고 볼 수 있겠네요.

그렇다면 무엇이 문제일까요?
일단, 패키지에 포함된 기본 js 경로부터 일단 맞지 않는 것 같고요.
그런데 이 부분은 _content/ 가상 경로에 생성되는 친구라, 제가 제어 할 수 있을지도 모르겠네요.
파일 구조가 바뀐 탓이 제일 큰 것 같은데 말이죠.

그래서 남은 사항 시도 방법이
npm에서 fast 패키지를 직접 받아서 css 빌드를 하고,
js를 직접 땡겨 쓰자 정도인데,

굳이 이렇게까지 해야 하나…?

라는 생각이 들어서 잠깐 멈춘 상태입니다.

제 개인 도메인 메인 페이지를 실습도 해볼 겸 blazor 새 버전으로 해보려다가,
이런 참사를 겪게 되네요 :frowning:

+++

아 참, 이 주소는

Fluent UI Blazor components (fluentui-blazor.net)
여기로 도메인 연결이 되었더라고요!
혹시 데모 보실 분 계시면 참고 부탁드립니다.

4개의 좋아요

Blazor Webassembly로 테스트했을 때는 잘 되었어서…

@suwoo 테스트한 템플릿이 Blazor 웹앱 프로젝트 템플릿이군요! 저도 해봤는데… 안되네요.
아마도 렌더링 하는 방식이 달라 그런 것 같은데요, 안되는 것이 맞는 것 같습니다.

Blazor 웹앱에서 서버 렌더링 또는 클라이언트 렌더링을 각각 따로 선택해서 테스트를 저도 해볼께요

3개의 좋아요

서버, 웹어셈블리를 혼합하지 않고 (Blazor 웹앱 프로젝트 생성 시 초기 체크박스로 선택할 수 있습니다) 둘 다 잘 동작하는 것을 확인할 수 있었는데요, 제가 한 것은,

| csproj에 패키지 추가

  <ItemGroup>
    <PackageReference Include="Microsoft.Fast.Components.FluentUI" Version="3.0.1" />
  </ItemGroup>

| App.razor에 reboot.css 추가

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <base href="/" />
    <link href="_content/Microsoft.Fast.Components.FluentUI/css/reboot.css" rel="stylesheet" />

| Counter.razor에 FluentButton추가

<FluentButton Appearance="Appearance.Accent" @onclick="IncrementCount">Click me</FluentButton>

| 동작 화면

※ 참고로 왼쪽 Nav 스타일이 안맞는것은 Bootstrap을 제거했기 때문입니다.

3개의 좋아요

답변 감사합니다.
저도 한번 다시 해볼게요!

image
재미있게도, 버튼 css 적용이 안 된 채로 클릭 이벤트는 발생하네요.

3개의 좋아요

신기하네요. 스타일을 보니 기본 스타일은 적용이 된 듯 한데요, (reboot.css 스타일일 수도 있어요) 버튼 어디갔지;;;

3개의 좋아요

@suwoo 확인해보니 Blazor 웹앱 혼합 모드에서도 동작을 잘 합니다. 안된다고 하셔서 급하게 테스트 하다보니 잘못 확인하였네요. 역시 잘 동작합니다.

간단히 테스트한 코드 깃허브로 공유합니다.

3개의 좋아요

아 이상한 게 있네요. 혼합 모드에서 prerendering 시 Badge가 이상하게 출력되네요! 이것 Fast Blazor 팀에게 문의를 해봐야겠습니다.

3개의 좋아요
2개의 좋아요

제가 겪은 것과 거의 같은 증상인 듯 하여 링크 걸어 봅니다 ㅎㅎ ㅠㅠ

RC2까지 이슈를 block 한다고 하네요.

Hi,

See also #706. We are blocked on this till .NET 8 RC2. The issue with MainLayout/@Attribute will be addressed then.

2개의 좋아요

저 같은 경우는 asp.net core 7.0 blazor server 기준이기는 한데, 저는 템플릿 이용하지 않고 패키지(3.0.1)를 인스톨 해서 사용했습니다. 참고사항으로 내용을 남겨봅니다.

FluentTitle css가 제대로 적용되지 않길래 템플릿과의 차이점을 확인해봤더니…
_Host.cshtml >> head 태그에 파일이 프로젝트에 없더라도

<link href="{프로젝트명}.styles.css" rel="stylesheet">

이 부분을 추가해야 했습니다.
렌더링 될때마다 해당 페이지의 css가 생성되어 삽입되는 것 같습니다.
자동생성된 css 내용은 다음과 같습니다.

@import '_content/Microsoft.Fast.Components.FluentUI/Microsoft.Fast.Components.FluentUI.bundle.scp.css';

[b-bepfy0c0x7] > fluent-dialog::part(control) {
    --dialog-width: 1200px;
    --dialog-height: 700px;
    padding: 2rem;
    background-color: white;
}

[b-bepfy0c0x7] > fluent-dialog .fluent-dialog-content .fluent-dialog-body {
    margin-left: 0px;
    margin-right: 0px;
    width: 100%;
}

import되는 Microsoft.Fast.Components.FluentUI.bundle.scp.css >> 이 파일은 Fluent Component 및 웹컴포넌트에 대한 css 파일이고 아래 css 코드는 해당 페이지에 대한 css 격리가 처리된 부분입니다.

2개의 좋아요

넵! 사용 하신 방법이 맞습니다.
7.0기준으로는 별 문제 없이 작동됩니다.

  • 8.0에선 저 css파일 참조가 자동으로 들어가요.
2개의 좋아요

그렇군요~~^^

2개의 좋아요

내용 쬐끔 덧붙입니다.

이 이슈를 보면 저와 같은 증상으로 고통 받는(?)분들이 꽤 있었는데요,

해당 이슈 해결되는 변경점이 Merge 된 걸로 보이네요!

이쪽은 VS Preview의 템플릿 생성 가독성 관련 제안 인 듯 해요.

음, 그렇다면 RC2가 릴리즈 되면 해당 증상이 해결되었는지 확인 해봐야겠군요.

3개의 좋아요

약간 Blazor Template 자체의 문제와 겹치는 분이 많은 것 같아
이 이상 여기 붙이는 건 정보의 범람 같기도 하고,
제 경험담 공유를 위해 글을 따로 작성해보았습니다.

먼저 이런 시도를 하게 해주신 양질의 글쓴이 여러분 너무 고맙습니다!

3개의 좋아요