Blazor WebAssembly 성능 최적화
Blazor WebAssembly는 C#을 브라우저에서 실행하는 강력한 프레임워크지만, 초기 로딩 크기와 렌더링 성능이 주요 최적화 대상입니다. .NET 8에서는 AOT 컴파일, Static SSR, Streaming 렌더링이 추가되어 성능이 크게 개선되었습니다.
1. 번들 크기 최소화
섹션 제목: “1. 번들 크기 최소화”<PropertyGroup> <!-- IL Trimming: 사용하지 않는 코드 제거 --> <PublishTrimmed>true</PublishTrimmed> <TrimMode>full</TrimMode>
<!-- AOT 컴파일: WASM 네이티브 코드로 변환 --> <RunAOTCompilation>true</RunAOTCompilation>
<!-- 압축 --> <BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport> <InvariantGlobalization>true</InvariantGlobalization></PropertyGroup># 게시 빌드 (최적화 최대)dotnet publish -c Release
# AOT 포함 (빌드 오래 걸리나 런타임 빠름)dotnet publish -c Release -p:RunAOTCompilation=true2. Lazy Loading — 어셈블리 지연 로드
섹션 제목: “2. Lazy Loading — 어셈블리 지연 로드”builder.Services.AddScoped<LazyAssemblyLoader>();
// 라우팅에서 Lazy 어셈블리 지정// App.razor<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@lazyLoadedAssemblies"> ...</Router>
@code { private List<Assembly> lazyLoadedAssemblies = new();
protected override async Task OnInitializedAsync() { // 특정 경로 진입 시에만 어셈블리 로드 Router.OnNavigateAsync = NavigationHandler; }
private async Task NavigationHandler(NavigationContext ctx) { if (ctx.Path.StartsWith("/admin")) { var assemblies = await LazyLoader.LoadAssembliesAsync( new[] { "AdminModule.dll" }); lazyLoadedAssemblies.AddRange(assemblies); } }}3. 렌더링 최적화 — ShouldRender
섹션 제목: “3. 렌더링 최적화 — ShouldRender”@* CounterDisplay.razor *@@inherits ComponentBase
<p>Count: @Count</p>
@code { [Parameter] public int Count { get; set; } private int _lastRenderedCount = -1;
// 실제로 변경된 경우에만 다시 렌더링 protected override bool ShouldRender() { if (_lastRenderedCount == Count) return false; _lastRenderedCount = Count; return true; }}4. Virtualization — 대용량 목록
섹션 제목: “4. Virtualization — 대용량 목록”@* 10만 건도 부드럽게 렌더링 *@<div style="height: 500px; overflow-y: auto;"> <Virtualize Items="@AllItems" Context="item" OverscanCount="3"> <ItemContent> <div class="item-row"> <span>@item.Id</span> <span>@item.Name</span> </div> </ItemContent> <Placeholder> <div class="loading-placeholder">로딩 중...</div> </Placeholder> </Virtualize></div>
@* 서버 페이지네이션과 결합 *@<Virtualize Context="item" ItemsProvider="@LoadItems" ItemSize="50"> <div>@item.Name</div></Virtualize>
@code { private async ValueTask<ItemsProviderResult<Item>> LoadItems( ItemsProviderRequest req) { var result = await Api.GetPageAsync(req.StartIndex, req.Count); return new(result.Items, result.TotalCount); }}5. JS Interop 최적화
섹션 제목: “5. JS Interop 최적화”// ❌ 느림: 매 호출마다 JS 평가await JSRuntime.InvokeVoidAsync("console.log", message);
// ✅ 빠름: 모듈 사전 로드 + IJSObjectReference 캐시public class JsInteropService : IAsyncDisposable{ private readonly Lazy<Task<IJSObjectReference>> _module;
public JsInteropService(IJSRuntime js) { _module = new(() => js.InvokeAsync<IJSObjectReference>( "import", "./js/myModule.js").AsTask()); }
public async ValueTask LogAsync(string msg) { var m = await _module.Value; await m.InvokeVoidAsync("log", msg); }
public async ValueTask DisposeAsync() { if (_module.IsValueCreated) await (await _module.Value).DisposeAsync(); }}6. .NET 8 Static SSR + Enhanced Navigation
섹션 제목: “6. .NET 8 Static SSR + Enhanced Navigation”// Program.cs (.NET 8)builder.Services.AddRazorComponents() .AddInteractiveWebAssemblyComponents();
// App.razor<Routes @rendermode="InteractiveWebAssembly" />
// 개별 페이지 렌더 모드 지정@page "/counter"@rendermode InteractiveWebAssembly
// 정적 페이지 (서버 렌더, JS 없음)@page "/about"// rendermode 없음 → Static SSR7. 캐싱 전략
섹션 제목: “7. 캐싱 전략”// HttpClient 응답 캐싱builder.Services.AddScoped(sp =>{ var http = new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }; return http;});
// 컴포넌트 수준 캐싱@code { private static readonly Dictionary<int, Product> _cache = new();
protected override async Task OnInitializedAsync() { if (!_cache.TryGetValue(Id, out var product)) { product = await Http.GetFromJsonAsync<Product>($"api/products/{Id}"); _cache[Id] = product!; } Product = product; }}Blazor WASM 최적화의 핵심은 번들 크기(Trimming + AOT), 렌더링 횟수(ShouldRender), 목록 성능(Virtualize), JS Interop 비용(모듈 캐시) 네 가지입니다. .NET 8에서는 Static SSR과 WebAssembly 렌더링을 페이지별로 혼용해 초기 로딩은 서버 렌더로 빠르게 하고, 인터랙티브 부분만 WASM으로 처리하세요.