diff --git a/src/TaxBaik.Web.Client/Components/Admin/Pages/Login.razor b/src/TaxBaik.Web.Client/Components/Admin/Pages/Login.razor
index 9db8e3d..1524f6c 100644
--- a/src/TaxBaik.Web.Client/Components/Admin/Pages/Login.razor
+++ b/src/TaxBaik.Web.Client/Components/Admin/Pages/Login.razor
@@ -1,6 +1,6 @@
@page "/admin/login"
@layout TaxBaik.WasmClient.Components.Admin.Layout.BlankLayout
@attribute [AllowAnonymous]
-@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
+@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: true))
로그인
diff --git a/src/TaxBaik.Web.Client/Components/Admin/Shared/AdminLoginForm.razor b/src/TaxBaik.Web.Client/Components/Admin/Shared/AdminLoginForm.razor
index 51bef7c..39636c5 100644
--- a/src/TaxBaik.Web.Client/Components/Admin/Shared/AdminLoginForm.razor
+++ b/src/TaxBaik.Web.Client/Components/Admin/Shared/AdminLoginForm.razor
@@ -1,57 +1,47 @@
-@using System.Text.Json
@inject ILocalStorageService LocalStorageService
-@inject NavigationManager Navigation
-@inject ISnackbar Snackbar
-@inject HttpClient Http
-@inject AuthenticationStateProvider AuthStateProvider
+@inject IJSRuntime Js
관리자 로그인
-
-
+
+ 로그인 중 오류가 발생했습니다.
+
+
+
@code {
- private MudForm? form;
- private string username = "";
- private string password = "";
- private bool rememberMe = false;
- private bool isLoggingIn = false;
+ private string rememberedUsername = "";
+ private bool isRememberChecked = false;
+ private bool isReady;
private const string RememberedUsernameKey = "admin-remembered-username";
private const string RememberedCheckboxKey = "admin-remember-checkbox";
@@ -59,83 +49,35 @@
{
try
{
- username = await LocalStorageService.GetItemAsStringAsync(RememberedUsernameKey) ?? "";
+ rememberedUsername = await LocalStorageService.GetItemAsStringAsync(RememberedUsernameKey) ?? "";
var checkboxValue = await LocalStorageService.GetItemAsStringAsync(RememberedCheckboxKey) ?? "false";
- rememberMe = checkboxValue == "true" && !string.IsNullOrEmpty(username);
+ isRememberChecked = checkboxValue == "true" && !string.IsNullOrEmpty(rememberedUsername);
}
catch
{
- username = "";
- rememberMe = false;
+ rememberedUsername = "";
+ isRememberChecked = false;
}
}
- private async Task HandleLogin()
+ protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (form == null || isLoggingIn) return;
-
- await form.Validate();
- if (!form.IsValid) return;
-
- isLoggingIn = true;
- try
+ if (firstRender)
{
- var response = await Http.PostAsJsonAsync("/api/auth/login", new { username, password });
- if (!response.IsSuccessStatusCode)
+ try
{
- Snackbar.Add("로그인 실패: 사용자명 또는 비밀번호가 올바르지 않습니다", Severity.Error);
- return;
+ await Js.InvokeVoidAsync("taxbaikAdminSession.syncRouteClass");
+ await Js.InvokeVoidAsync("taxbaikAdminSession.bindLoginForm");
}
-
- var json = await response.Content.ReadAsStringAsync();
- var loginResponse = JsonSerializer.Deserialize(json);
- if (loginResponse == null || string.IsNullOrEmpty(loginResponse.AccessToken))
+ catch
{
- Snackbar.Add("로그인 실패: 응답 오류", Severity.Error);
- return;
+ // Login UI must remain visible even if JS binding fails.
}
-
- // 로컬 저장소 저장
- await LocalStorageService.SetItemAsStringAsync("accessToken", loginResponse.AccessToken);
- await LocalStorageService.SetItemAsStringAsync("refreshToken", loginResponse.RefreshToken ?? "");
- var expiryTicks = DateTimeOffset.UtcNow.AddSeconds(loginResponse.ExpiresIn).UtcTicks;
- await LocalStorageService.SetItemAsStringAsync("tokenExpiry", expiryTicks.ToString());
-
- // 아이디 저장
- if (rememberMe)
+ finally
{
- await LocalStorageService.SetItemAsStringAsync(RememberedUsernameKey, username);
- await LocalStorageService.SetItemAsStringAsync(RememberedCheckboxKey, "true");
+ isReady = true;
+ StateHasChanged();
}
- else
- {
- await LocalStorageService.RemoveItemAsync(RememberedUsernameKey);
- await LocalStorageService.RemoveItemAsync(RememberedCheckboxKey);
- }
-
- // Blazor 인증 상태 업데이트
- if (AuthStateProvider is CustomAuthenticationStateProvider customProvider)
- {
- await customProvider.LoginAsync(loginResponse.AccessToken, loginResponse.RefreshToken ?? "", loginResponse.ExpiresIn);
- }
-
- // 대시보드로 이동
- Navigation.NavigateTo("/admin/dashboard", replace: true);
}
- catch (Exception ex)
- {
- Snackbar.Add($"로그인 중 오류: {ex.Message}", Severity.Error);
- }
- finally
- {
- isLoggingIn = false;
- }
- }
-
- private class LoginResponse
- {
- public string AccessToken { get; set; } = "";
- public string RefreshToken { get; set; } = "";
- public int ExpiresIn { get; set; }
}
}
diff --git a/src/TaxBaik.Web/wwwroot/js/admin-session.js b/src/TaxBaik.Web/wwwroot/js/admin-session.js
index 1e01c36..7ae9502 100644
--- a/src/TaxBaik.Web/wwwroot/js/admin-session.js
+++ b/src/TaxBaik.Web/wwwroot/js/admin-session.js
@@ -341,10 +341,11 @@ window.taxbaikAdminSession = {
localStorage.removeItem('admin-remember-checkbox');
}
- // 토큰을 저장한 직후 페이지 리로드
- // 이렇게 하면 CustomAuthenticationStateProvider의 GetAuthenticationStateAsync()가
- // localStorage에서 토큰을 복원하고 [Authorize] 페이지가 제대로 렌더링됨
- window.location.href = '/taxbaik/admin/dashboard';
+ // 토큰 저장 후 약간의 지연을 두고 대시보드로 이동
+ // 이렇게 하면 CustomAuthenticationStateProvider가 localStorage에서 토큰을 복원할 시간이 생김
+ setTimeout(() => {
+ window.location.href = '/taxbaik/admin/dashboard';
+ }, 200);
} catch (error) {
window.taxbaikAdminSession.traceUiState('admin-login', `submit failed: ${error?.message || 'login failed'}`);
postLog({