feat: standalone Blazor WebAssembly admin + SEO enhancements

Architecture:
- Admin UI: /admin (Standalone Blazor WebAssembly, 219 WASM files)
- Portal: /portal (Razor Pages, Cookie/OAuth auth)
- Homepage: / (Razor Pages, SSR)
- API: /api (FastEndpoints + JWT)

SEO:
- Sitemap: Public content only (blog, FAQ, announcements, contact)
- robots.txt: Exclude /admin and /portal, reference production domain
- Naver verification: naverb1813cd79ddc2ded5c5291fca5cb46c2.html ready

Technical:
- TaxBaik.Web.Client: StaticWebAssetBasePath=admin
- Server Program.cs: UseBlazorFrameworkFiles + MapFallback for SPA routing
- base href="/admin/" for client-side navigation
- blazor.webassembly.js (standalone, not web.js)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 04:03:18 +09:00
parent 64e462e57e
commit 54367696dc
112 changed files with 2701 additions and 207 deletions
+12 -50
View File
@@ -20,9 +20,7 @@ using TaxBaik.Application.Seasonal;
using TaxBaik.Application.Utils;
using TaxBaik.Infrastructure;
using TaxBaik.Web.Services;
using TaxBaik.Web.Components.Admin.Services;
using TaxBaik.Web.Components.Admin.Services.AdminClients;
using TaxBaik.Web.Components.Admin.Shared;
// Client (WASM) 서비스는 Client 프로젝트에서만 사용됨
var builder = WebApplication.CreateBuilder(args);
var isProduction = builder.Environment.IsProduction();
@@ -90,14 +88,10 @@ builder.Services.AddRateLimiter(options =>
});
});
// Razor Pages + Blazor WebAssembly 통합
// Razor Pages
builder.Services.AddRazorPages();
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.Configure<Microsoft.AspNetCore.Components.Server.CircuitOptions>(options =>
{
options.DetailedErrors = true;
});
// Admin Client는 standalone Blazor WebAssembly로 호스팅 (별도 정적 파일)
// Razor Component 렌더링은 사용하지 않음
// Session & TempData (쿠키 저장소)
builder.Services.AddSession(options =>
@@ -282,35 +276,7 @@ builder.Services.AddHttpClient<IAnnouncementBrowserClient, AnnouncementBrowserCl
}).AddHttpMessageHandler<TokenRefreshHandler>();
// Phase 5: Tax Accounting & CRM Browser Clients
builder.Services.AddHttpClient<ITaxProfileBrowserClient, TaxProfileBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<ITaxFilingScheduleBrowserClient, TaxFilingScheduleBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<IConsultingActivityBrowserClient, ConsultingActivityBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<IContractBrowserClient, ContractBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<IRevenueTrackingBrowserClient, RevenueTrackingBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
builder.Services.AddHttpClient<ICommonCodeBrowserClient, CommonCodeBrowserClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<TokenRefreshHandler>();
// (등록은 Client 프로젝트에서만 필요 - Server는 API만 제공)
// UI & 캐시 (MudBlazor Theme Customization)
builder.Services.AddMudServices(config =>
@@ -412,23 +378,19 @@ if (!app.Environment.IsDevelopment())
app.UseHsts();
}
// API + Razor Pages + Blazor 매핑
// API + Razor Pages + 정적 파일 매핑
app.MapControllers();
app.MapFastEndpoints();
app.MapHealthChecks("/healthz");
app.MapRazorPages();
app.MapStaticAssets();
// AllowAnonymous: JWT 미들웨어가 Blazor 셸 요청을 401로 차단하지 않도록 한다.
// 인증은 Blazor AuthorizeRouteView → RedirectToLogin 에서 처리한다.
// Phase 8: WebAssembly 렌더 모드 완전 마이그레이션
// - App.razor: TaxBaik.Web (메인 웹 서버)
// - Routes + Pages + Shared + Layout + Forms: TaxBaik.Web (메인 웹 서버)
// 모든 Blazor 컴포넌트가 웹 서버에서 통합 서비스됨
// API는 웹 서버에서만 제공 (클라이언트 프로젝트 분리 불필요)
app.MapRazorComponents<TaxBaik.Web.Components.Admin.App>()
.AddInteractiveServerRenderMode()
.AllowAnonymous();
// Admin Blazor WebAssembly SPA 호스팅 (/admin)
app.UseBlazorFrameworkFiles("/admin");
app.UseStaticFiles("/admin");
// /admin 라우팅 폴백 (SPA 라우트 처리)
app.MapFallbackToFile("admin/{*path:nonfile}", "admin/index.html");
// 애플리케이션 시작/종료 로깅
try