feat(auth): implement option 3 architecture - server InteractiveServer login + client WASM dashboard

- Add Server.AddInteractiveServerComponents() + AddInteractiveServerRenderMode()
- Create Server AuthLayout for login form (MudBlazor)
- Implement LoginSimple.razor with @rendermode InteractiveServer in Server project
- Update App.razor: CascadingAuthenticationState + Router with AppAssembly=Server
- Fix Client MudBlazor Providers in MainLayout + AuthLayout
- Update Playwright tests: use dynamic selectors for MudTextField (auto-generated IDs)
- Build: 0 errors, 0 warnings
- API: /api/auth/login fully operational (200 OK confirmed)
- Playwright E2E test: 1 passed

Architecture:
/login    → Server InteractiveServer (MudBlazor form)
/         → Client WebAssembly (Dashboard)
/api/*    → Server Endpoints

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 23:10:54 +09:00
parent 98501c0d2f
commit 53db2f63e3
27 changed files with 1012 additions and 296 deletions
+64
View File
@@ -0,0 +1,64 @@
import { test } from '@playwright/test';
test('브라우저 콘솔 에러 확인', async ({ page }) => {
const consoleMessages: string[] = [];
const jsErrors: string[] = [];
page.on('console', msg => {
console.log(`[${msg.type().toUpperCase()}] ${msg.text()}`);
if (msg.type() === 'error') {
jsErrors.push(msg.text());
}
consoleMessages.push(`${msg.type()}: ${msg.text()}`);
});
page.on('pageerror', error => {
console.log(`[PAGE ERROR] ${error.message}`);
jsErrors.push(error.message);
});
console.log('\n로그인 페이지 접근...');
await page.goto('http://localhost:5265/login');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
console.log('\n입력 및 로그인...');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]');
const loginButton = page.locator('button:has-text("로그인")');
await usernameInput.fill('admin');
await passwordInput.fill('admin');
console.log('로그인 버튼 클릭...');
await loginButton.click();
await page.waitForTimeout(3000);
console.log('\n\n╔════════════════════════════════════════════════════╗');
console.log('║ 콘솔 메시지 요약 ║');
console.log('╚════════════════════════════════════════════════════╝\n');
console.log(`총 메시지: ${consoleMessages.length}`);
console.log(`JavaScript 에러: ${jsErrors.length}`);
if (jsErrors.length > 0) {
console.log('\n❌ 감지된 에러:');
jsErrors.forEach((err, i) => {
console.log(` ${i + 1}. ${err}`);
});
} else {
console.log('\n✅ JavaScript 에러 없음');
}
// Blazor 관련 콘솔 메시지
const blazorMessages = consoleMessages.filter(m => m.toLowerCase().includes('blazor'));
if (blazorMessages.length > 0) {
console.log('\n🔵 Blazor 메시지:');
blazorMessages.forEach(msg => {
console.log(` - ${msg}`);
});
}
console.log('\n최종 URL:', page.url());
});