[] 로딩 인디케이터 추가

This commit is contained in:
김선규 2025-06-05 16:24:16 +09:00
parent 0698f65ddf
commit 1649818434
9 changed files with 131 additions and 23 deletions

View File

@ -31,6 +31,9 @@ builder.Services.AddScoped(sp => //new HttpClient
// SCOPED 으로 등록된 서비스는 DI 컨테이너에 등록된 서비스의 인스턴스를 사용합니다. // SCOPED 으로 등록된 서비스는 DI 컨테이너에 등록된 서비스의 인스턴스를 사용합니다.
builder.Services.AddScoped<APIService>(); builder.Services.AddScoped<APIService>();
builder.Services.AddScoped<CookieService>(); builder.Services.AddScoped<CookieService>();
// builder.Services.AddRazorPages();
// builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<LoadingService>();
await builder.Build().RunAsync(); await builder.Build().RunAsync();

View File

@ -1,23 +1,5 @@
@inherits LayoutComponentBase @inherits LayoutComponentBase
@* *@ @implements IDisposable
@* *@
@* <div class="min-h-screen bg-gray-50 text-gray-900"> *@
@* <TopBanner /> *@
@* <TopNav /> *@
@* *@
@* <div class="flex flex-1"> *@
@* <SideNav /> *@
@* *@
@* <main class="flex-1 p-6"> *@
@* $1$ <!-- Body는 URL 뒤에 입력할 페이지에 따라서 그거에 맞는 @page를 찾아서 열어준다. --> #1# *@
@* @Body *@
@* </main> *@
@* </div> *@
@* *@
@* <FloatingButton /> *@
@* <BottomNav /> *@
@* <Footer/> *@
@* </div> *@
<div class="min-h-screen flex flex-col bg-gray-50 text-gray-900"> <div class="min-h-screen flex flex-col bg-gray-50 text-gray-900">
@ -49,4 +31,13 @@
<!-- 하단 메뉴 --> <!-- 하단 메뉴 -->
<BottomNav /> <BottomNav />
<Footer /> <Footer />
@if (LoadingService.IsLoading)
{
<div class="fixed inset-0 bg-black/80 flex items-center justify-center z-50">
<div class="bg-gray-200/60 px-6 py-4 rounded-lg">
<div class="animate-spin h-8 w-8 border-4 border-gray-600 border-t-transparent rounded-full"></div>
</div>
</div>
}
</div> </div>

View File

@ -1,13 +1,49 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Front.Program.Views.Project;
using Front.Program.Services;
namespace Front.Program.Layout; namespace Front.Program.Layout;
public partial class MainLayout: LayoutComponentBase public partial class MainLayout : LayoutComponentBase, IDisposable
{ {
[Inject] [Inject]
NavigationManager Navigation { get; set; } = default!; NavigationManager Navigation { get; set; } = default!;
[Inject]
LoadingService LoadingService { get; set; } = default!;
// protected bool isHideTop => Navigation.Uri.Contains("/auth"); // protected bool isHideTop => Navigation.Uri.Contains("/auth");
protected bool isHideTop => Navigation.ToBaseRelativePath(Navigation.Uri).Equals("auth", StringComparison.OrdinalIgnoreCase); protected bool isHideTop => Navigation.ToBaseRelativePath(Navigation.Uri).Equals("auth", StringComparison.OrdinalIgnoreCase);
public static bool IsLoading { get; set; }
public static IndicateType CurrentType { get; set; } = IndicateType.Page;
protected override void OnInitialized()
{
LoadingService.OnChange += StateHasChanged;
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
LoadingService.HideNavigationLoading();
}
public void Dispose()
{
LoadingService.OnChange -= StateHasChanged;
Navigation.LocationChanged -= HandleLocationChanged;
}
public static void ShowLoading(IndicateType type = IndicateType.Page)
{
IsLoading = true;
CurrentType = type;
}
public static void HideLoading()
{
IsLoading = false;
}
} }

View File

@ -0,0 +1,42 @@
using Front.Program.Views.Project;
using Microsoft.AspNetCore.Components;
namespace Front.Program.Services;
public class LoadingService
{
public bool IsLoading { get; private set; }
public IndicateType CurrentType { get; private set; } = IndicateType.Page;
private bool isNavigationLoading { get; set; }
public event Action? OnChange;
public void ShowLoading(IndicateType type = IndicateType.Page, bool isNavigation = false)
{
IsLoading = true;
CurrentType = type;
isNavigationLoading = isNavigation;
NotifyStateChanged();
}
public void HideLoading()
{
if (!isNavigationLoading)
{
IsLoading = false;
NotifyStateChanged();
}
}
public void HideNavigationLoading()
{
if (isNavigationLoading)
{
IsLoading = false;
isNavigationLoading = false;
NotifyStateChanged();
}
}
private void NotifyStateChanged() => OnChange?.Invoke();
}

View File

@ -9,18 +9,18 @@ namespace Front.Program.Views.Project;
public partial class Auth : ComponentBase public partial class Auth : ComponentBase
{ {
[Inject] NavigationManager NavigationManager { get; set; } = default!; [Inject] NavigationManager NavigationManager { get; set; } = default!;
[Inject] LoadingService LoadingService { get; set; } = default!;
// [Inject] IJSRuntime JS { get; set; } = default!; // [Inject] IJSRuntime JS { get; set; } = default!;
// [Inject] CookieService Cookie { get; set; } = default!; // [Inject] CookieService Cookie { get; set; } = default!;
[Inject] HttpClient Http { get; set; } = default!; [Inject] HttpClient Http { get; set; } = default!;
public async Task KakaoLogin() public async Task KakaoLogin()
{ {
LoadingService.ShowLoading();
var url = "/api/v1/out/user/kakao/auth"; var url = "/api/v1/out/user/kakao/auth";
var response = await Http.GetFromJsonAsync<JsonElement>(url); var response = await Http.GetFromJsonAsync<JsonElement>(url);
var kakaoUrl = response.GetProperty("url").GetString(); var kakaoUrl = response.GetProperty("url").GetString();
Console.WriteLine(kakaoUrl); Console.WriteLine(kakaoUrl);
if (!string.IsNullOrEmpty(kakaoUrl)) if (!string.IsNullOrEmpty(kakaoUrl))
{ {
NavigationManager.NavigateTo(kakaoUrl, true); NavigationManager.NavigateTo(kakaoUrl, true);

View File

@ -0,0 +1,11 @@
<h3>PageIndicator</h3>
@if (Type == IndicateType.Page)
{
<div class="fixed top-0 left-0 w-full h-14 bg-black/70 flex items-center justify-center z-50">
<div class="bg-gray-200/80 px-4 py-2 rounded-lg">
<div class="animate-spin h-5 w-5 border-2 border-gray-600 border-t-transparent rounded-full"></div>
</div>
</div>
}

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace Front.Program.Views.Project;
public partial class PageIndicator : ComponentBase
{
[Parameter]
public IndicateType Type { get; set; } = IndicateType.Page;
}
public enum IndicateType
{
Page,
Circle,
Progress
}

View File

@ -4,6 +4,7 @@ using System.Text.Json;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Http.Json; using System.Net.Http.Json;
using Front.Program.Layout;
namespace Front.Program.Views.Project; namespace Front.Program.Views.Project;
@ -138,6 +139,7 @@ public partial class Register : ComponentBase
try try
{ {
MainLayout.ShowLoading();
// 쿠키에서 토큰 가져오기 // 쿠키에서 토큰 가져오기
var token = await JS.InvokeAsync<string>("eval", "document.cookie.split('; ').find(row => row.startsWith('Web_AM_Connect_Key='))?.split('=')[1] || ''"); var token = await JS.InvokeAsync<string>("eval", "document.cookie.split('; ').find(row => row.startsWith('Web_AM_Connect_Key='))?.split('=')[1] || ''");
Console.WriteLine($"쿠키에서 가져온 토큰: '{token}'"); Console.WriteLine($"쿠키에서 가져온 토큰: '{token}'");
@ -146,6 +148,8 @@ public partial class Register : ComponentBase
{ {
await JS.InvokeVoidAsync("alert", "인증 정보가 없습니다."); await JS.InvokeVoidAsync("alert", "인증 정보가 없습니다.");
NavigationManager.NavigateTo("/"); NavigationManager.NavigateTo("/");
MainLayout.HideLoading();
return; return;
} }
@ -156,6 +160,7 @@ public partial class Register : ComponentBase
request.Content = JsonContent.Create(registerData); request.Content = JsonContent.Create(registerData);
var response = await Http.SendAsync(request); var response = await Http.SendAsync(request);
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
var result = await response.Content.ReadFromJsonAsync<JsonElement>(); var result = await response.Content.ReadFromJsonAsync<JsonElement>();
@ -181,16 +186,19 @@ public partial class Register : ComponentBase
// 세션 스토리지 정리 // 세션 스토리지 정리
await JS.InvokeVoidAsync("sessionStorage.removeItem", "snsId"); await JS.InvokeVoidAsync("sessionStorage.removeItem", "snsId");
MainLayout.HideLoading();
await JS.InvokeVoidAsync("alert", "회원가입이 완료되었습니다."); await JS.InvokeVoidAsync("alert", "회원가입이 완료되었습니다.");
NavigationManager.NavigateTo("/"); NavigationManager.NavigateTo("/");
} }
else else
{ {
MainLayout.HideLoading();
await JS.InvokeVoidAsync("alert", $"회원가입 실패: {message}"); await JS.InvokeVoidAsync("alert", $"회원가입 실패: {message}");
} }
} }
else else
{ {
MainLayout.HideLoading();
Console.WriteLine($"API 호출 실패: {response.StatusCode}"); Console.WriteLine($"API 호출 실패: {response.StatusCode}");
var errorContent = await response.Content.ReadAsStringAsync(); var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"에러 내용: {errorContent}"); Console.WriteLine($"에러 내용: {errorContent}");
@ -199,6 +207,7 @@ public partial class Register : ComponentBase
} }
catch (Exception ex) catch (Exception ex)
{ {
MainLayout.HideLoading();
Console.WriteLine($"예외 발생: {ex.Message}"); Console.WriteLine($"예외 발생: {ex.Message}");
await JS.InvokeVoidAsync("alert", $"오류가 발생했습니다: {ex.Message}"); await JS.InvokeVoidAsync("alert", $"오류가 발생했습니다: {ex.Message}");
} }

File diff suppressed because one or more lines are too long