From cd8270c5c0bcadd055215a255563be17f162d92f Mon Sep 17 00:00:00 2001 From: SEAN Date: Mon, 9 Feb 2026 16:25:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20DI=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EB=B0=8F=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=EC=A1=B0=ED=99=94=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Infrastructure/DependencyInjection.cs: AddInfrastructure() 확장 메서드 - Application/DependencyInjection.cs: AddApplication() 확장 메서드 - API/Extensions/ApplicationBuilderExtensions.cs: UseMiddlewarePipeline() 확장 메서드 - Program.cs 정리 (DI/파이프라인 분리) Closes #26 --- .../ApplicationBuilderExtensions.cs | 80 +++++++++++++++++++ SPMS.API/Program.cs | 78 ++---------------- SPMS.Application/DependencyInjection.cs | 14 ++++ SPMS.Application/SPMS.Application.csproj | 4 + SPMS.Infrastructure/DependencyInjection.cs | 32 ++++++++ 5 files changed, 138 insertions(+), 70 deletions(-) create mode 100644 SPMS.API/Extensions/ApplicationBuilderExtensions.cs create mode 100644 SPMS.Application/DependencyInjection.cs create mode 100644 SPMS.Infrastructure/DependencyInjection.cs diff --git a/SPMS.API/Extensions/ApplicationBuilderExtensions.cs b/SPMS.API/Extensions/ApplicationBuilderExtensions.cs new file mode 100644 index 0000000..bda0774 --- /dev/null +++ b/SPMS.API/Extensions/ApplicationBuilderExtensions.cs @@ -0,0 +1,80 @@ +using Serilog; +using SPMS.API.Middlewares; + +namespace SPMS.API.Extensions; + +public static class ApplicationBuilderExtensions +{ + public static WebApplication UseMiddlewarePipeline(this WebApplication app) + { + // -- 1. 예외 처리 (최외곽 — 이후 모든 미들웨어 예외 포착) -- + app.UseMiddleware(); + + // -- 2. 보안 헤더 주입 (미구현) -- + // app.UseMiddleware(); + + // -- 3. X-Request-ID 발급/반환 (클라이언트 디버깅용) -- + app.UseMiddleware(); + + // -- 4. Serilog 구조적 로깅 (X-Request-ID 이후에 위치) -- + app.UseSerilogRequestLogging(options => + { + options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => + { + if (httpContext.Items.TryGetValue("RequestId", out var requestId)) + { + diagnosticContext.Set("RequestId", requestId); + } + }; + }); + + // -- 5. HTTPS 리다이렉션 (Nginx가 HTTPS 처리하므로 Production에서만) -- + if (!app.Environment.IsDevelopment()) + { + app.UseHttpsRedirection(); + } + + // -- 6. 정적 파일 서빙 -- + var webRoot = app.Environment.WebRootPath; + if (Directory.Exists(webRoot)) + { + app.UseStaticFiles(); + } + + // -- 7. 라우팅 -- + app.UseRouting(); + + // -- 8. CORS (미구현) -- + // app.UseCors("DefaultPolicy"); + + // -- 9. API 속도 제한 (미구현 — Issue #12) -- + // app.UseRateLimiter(); + + // -- 10. JWT 인증 -- + app.UseAuthentication(); + + // -- 11. 역할 인가 -- + app.UseAuthorization(); + + // -- 12. X-Service-Code 서비스 식별 (미구현 — Issue #13) -- + // app.UseMiddleware(); + + // -- 13. X-API-KEY 검증 (미구현 — Issue #13) -- + // app.UseMiddleware(); + + // -- 14. X-SPMS-TEST 샌드박스 모드 (미구현 — Issue #14) -- + // app.UseMiddleware(); + + // -- 15. OpenAPI (개발 환경만) -- + if (app.Environment.IsDevelopment()) + { + app.MapOpenApi(); + } + + // -- 16. 엔드포인트 매핑 -- + app.MapControllers(); + app.MapFallbackToFile("index.html"); + + return app; + } +} diff --git a/SPMS.API/Program.cs b/SPMS.API/Program.cs index 8f23235..5e52736 100644 --- a/SPMS.API/Program.cs +++ b/SPMS.API/Program.cs @@ -1,14 +1,7 @@ -using Microsoft.EntityFrameworkCore; using Serilog; using SPMS.API.Extensions; -using SPMS.API.Middlewares; -using SPMS.Application.Interfaces; -using SPMS.Domain.Interfaces; +using SPMS.Application; using SPMS.Infrastructure; -using SPMS.Infrastructure.Auth; -using SPMS.Infrastructure.Persistence; -using SPMS.Infrastructure.Persistence.Repositories; - var builder = WebApplication.CreateBuilder(new WebApplicationOptions { @@ -20,74 +13,19 @@ var builder = WebApplication.CreateBuilder(new WebApplicationOptions builder.Host.UseSerilog((context, config) => config.ReadFrom.Configuration(context.Configuration)); +// ===== 2. Services (DI) ===== +builder.Services.AddApplication(); +builder.Services.AddInfrastructure(builder.Configuration); + +// ===== 3. Presentation ===== builder.Services.AddControllers(); builder.Services.AddOpenApi(); - -var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); - -builder.Services.AddDbContext(options => - options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))); - -builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); -builder.Services.AddScoped(); -builder.Services.AddScoped(); - -// JWT 인증/인가 builder.Services.AddJwtAuthentication(builder.Configuration); builder.Services.AddAuthorizationPolicies(); var app = builder.Build(); -// ── 1. 예외 처리 (최외곽 — 이후 모든 미들웨어 예외 포착) ── -app.UseMiddleware(); - -// ── 3. X-Request-ID 발급/반환 (클라이언트 디버깅용) ── -app.UseMiddleware(); - -// ── 4. Serilog 구조적 로깅 (X-Request-ID 이후에 위치) ── -app.UseSerilogRequestLogging(options => -{ - options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => - { - if (httpContext.Items.TryGetValue("RequestId", out var requestId)) - { - diagnosticContext.Set("RequestId", requestId); - } - }; -}); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.MapOpenApi(); -} -var webRoot = app.Environment.WebRootPath; -Console.WriteLine($"[System] Web Root Path: {webRoot}"); - -if (Directory.Exists(webRoot)) -{ - app.UseStaticFiles(); -} -else -{ - Console.WriteLine("[Error] Web root folder not found!"); -} - -// ── 5. HTTPS 리다이렉션 (Nginx가 HTTPS 처리하므로 Production에서만) ── -if (!app.Environment.IsDevelopment()) -{ - app.UseHttpsRedirection(); -} -app.UseRouting(); - -// ── 10. JWT 인증 ── -app.UseAuthentication(); - -// ── 11. 역할 인가 ── -app.UseAuthorization(); - -// [엔드포인트 매핑] -app.MapControllers(); -app.MapFallbackToFile("index.html"); +// ===== 4. Middleware Pipeline ===== +app.UseMiddlewarePipeline(); app.Run(); diff --git a/SPMS.Application/DependencyInjection.cs b/SPMS.Application/DependencyInjection.cs new file mode 100644 index 0000000..8d6fdb6 --- /dev/null +++ b/SPMS.Application/DependencyInjection.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace SPMS.Application; + +public static class DependencyInjection +{ + public static IServiceCollection AddApplication(this IServiceCollection services) + { + // Application Services + // services.AddScoped(); + + return services; + } +} diff --git a/SPMS.Application/SPMS.Application.csproj b/SPMS.Application/SPMS.Application.csproj index e228bb9..4be501f 100644 --- a/SPMS.Application/SPMS.Application.csproj +++ b/SPMS.Application/SPMS.Application.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/SPMS.Infrastructure/DependencyInjection.cs b/SPMS.Infrastructure/DependencyInjection.cs new file mode 100644 index 0000000..7e7efa2 --- /dev/null +++ b/SPMS.Infrastructure/DependencyInjection.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using SPMS.Application.Interfaces; +using SPMS.Domain.Interfaces; +using SPMS.Infrastructure.Auth; +using SPMS.Infrastructure.Persistence; +using SPMS.Infrastructure.Persistence.Repositories; + +namespace SPMS.Infrastructure; + +public static class DependencyInjection +{ + public static IServiceCollection AddInfrastructure( + this IServiceCollection services, + IConfiguration configuration) + { + // DbContext + var connectionString = configuration.GetConnectionString("DefaultConnection"); + services.AddDbContext(options => + options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))); + + // UnitOfWork & Repositories + services.AddScoped(); + services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); + + // External Services + services.AddScoped(); + + return services; + } +} -- 2.45.1