f8679cafcb
TaxBaik CI/CD / build-and-deploy (push) Failing after 41s
1️⃣ HttpClient 서비스 추가 - IApiClient 인터페이스 구현 - GET, POST, PUT, DELETE 메서드 - JWT 토큰 자동 관리 - /taxbaik/api 경로 자동 처리 2️⃣ Razor Pages 리팩토링 - Pages/Index.cshtml.cs: API /api/blog 호출 - Pages/Blog/Index.cshtml.cs: API /api/blog, /api/category 호출 - Pages/Contact.cshtml.cs: API /api/inquiry 호출 - Service 의존성 제거 3️⃣ Blazor Components 리팩토링 - Login.razor: API /api/auth/login 호출로 변경 - BlogList.razor: API /api/blog/admin/all 호출로 변경 - Service 의존성 제거 아키텍처: View (Razor Pages + Blazor) ↓ HttpClient Controllers (REST API) ↓ Services (비즈니스 로직) ↓ Repository (DB) 테스트 결과: ✅ 홈페이지: 200 OK ✅ 블로그 페이지: 200 OK ✅ 문의 페이지: 200 OK ✅ 로그인 페이지: 200 OK ✅ API 엔드포인트 모두 작동 장점: • UI 리뉴얼 시 API 변경 불필요 • 모바일앱, 데스크톱 클라이언트 추가 가능 • 비즈니스 로직과 UI 완전 독립 • 테스트 가능한 구조 완성 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
122 lines
3.8 KiB
C#
122 lines
3.8 KiB
C#
using System.IO.Compression;
|
|
using System.Text;
|
|
using System.Text.Encodings.Web;
|
|
using System.Text.Unicode;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.AspNetCore.ResponseCompression;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using MudBlazor.Services;
|
|
using TaxBaik.Application;
|
|
using TaxBaik.Infrastructure;
|
|
using TaxBaik.Web.Services;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Controllers (API)
|
|
builder.Services.AddControllers();
|
|
|
|
// Razor Pages + Blazor Server 통합
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
|
|
|
// JWT 인증
|
|
var jwtKey = builder.Configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("Missing JWT SecretKey");
|
|
var key = Encoding.ASCII.GetBytes(jwtKey);
|
|
|
|
builder.Services.AddAuthentication(opts =>
|
|
{
|
|
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
})
|
|
.AddJwtBearer(opts =>
|
|
{
|
|
opts.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
|
ValidateIssuer = false,
|
|
ValidateAudience = false
|
|
};
|
|
});
|
|
|
|
// Blazor 인증
|
|
builder.Services.AddScoped<AuthService>();
|
|
builder.Services.AddScoped<CustomAuthenticationStateProvider>();
|
|
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomAuthenticationStateProvider>());
|
|
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
|
|
builder.Services.AddCascadingAuthenticationState();
|
|
builder.Services.AddAuthorizationCore();
|
|
|
|
// HTTP Client for API
|
|
builder.Services.AddHttpClient<IApiClient, ApiClient>();
|
|
|
|
// UI & 캐시
|
|
builder.Services.AddMudServices();
|
|
builder.Services.AddMemoryCache();
|
|
builder.Services.AddResponseCompression(opts => {
|
|
opts.Providers.Add<GzipCompressionProvider>();
|
|
});
|
|
|
|
// 한글 포함 다국어 문자를 유니코드 엔티티로 변환하지 않도록 설정
|
|
builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
|
|
|
|
builder.Services.AddInfrastructure();
|
|
builder.Services.AddApplication();
|
|
|
|
// Register version info
|
|
var versionInfo = new VersionInfo();
|
|
var versionFilePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "version.txt");
|
|
if (File.Exists(versionFilePath))
|
|
{
|
|
var lines = File.ReadAllLines(versionFilePath);
|
|
foreach (var line in lines)
|
|
{
|
|
if (line.StartsWith("Version:"))
|
|
versionInfo.Version = line.Substring("Version:".Length).Trim();
|
|
else if (line.StartsWith("Built:"))
|
|
versionInfo.Built = line.Substring("Built:".Length).Trim();
|
|
}
|
|
}
|
|
builder.Services.AddSingleton(versionInfo);
|
|
|
|
var app = builder.Build();
|
|
|
|
// Run migrations on startup (non-blocking for development)
|
|
try
|
|
{
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
var connectionFactory = scope.ServiceProvider.GetRequiredService<TaxBaik.Domain.Interfaces.IDbConnectionFactory>();
|
|
var cs = builder.Configuration.GetConnectionString("Default")
|
|
?? throw new InvalidOperationException("Missing connection string");
|
|
var migrationRunner = new TaxBaik.Infrastructure.Data.MigrationRunner(cs, connectionFactory);
|
|
await migrationRunner.RunAsync();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"⚠️ Migration warning (non-blocking): {ex.Message}");
|
|
}
|
|
|
|
app.UsePathBase("/taxbaik");
|
|
app.UseResponseCompression();
|
|
app.UseStaticFiles();
|
|
app.UseRouting();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
app.UseAntiforgery();
|
|
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/Error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
// API + Razor Pages + Blazor 매핑
|
|
app.MapControllers();
|
|
app.MapRazorPages();
|
|
app.MapRazorComponents<TaxBaik.Web.Components.Admin.App>().AddInteractiveServerRenderMode();
|
|
|
|
app.Run();
|