diff --git a/Back.csproj b/Back.csproj
index 65e2d3e..5d91557 100644
--- a/Back.csproj
+++ b/Back.csproj
@@ -8,8 +8,16 @@
+
-
+
+
+
+
+
+
+
+
diff --git a/Program.cs b/Program.cs
index bce62c2..22377a4 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,73 +1,98 @@
-//var builder = WebApplication.CreateBuilder(args);
-/*
-var options = new WebApplicationOptions
+
+using Pomelo.EntityFrameworkCore;
+
+using System.Text;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+
+using AcaMate.Common.Models;
+using AcaMate.V1.Services;
+using AcaMate.Common.Data;
+using AcaMate.V1.Controllers;
+
+
+var builder = WebApplication.CreateBuilder(args);
+
+// DB 설정부 시작
+builder.Configuration.AddJsonFile("private/dbSetting.json", optional: true, reloadOnChange: true);
+// var connectionString = builder.Configuration.GetConnectionString("MariaDbConnection");
+// builder.Services.AddDbContext(options => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
+builder.Services.AddDbContext(optionsAction: (serviceProvider, options) =>
{
- WebRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"
- ? "/src/publish/Debug/wwwroot"
- : "/src/publish/Release/wwwroot"
-};
-*/
+ var httpContextAccessor = serviceProvider.GetRequiredService();
+ var dbName = httpContextAccessor.HttpContext?.Request.Query["aca_code"].ToString();
+ var baseConnectionString = builder.Configuration.GetConnectionString("MariaDbConnection");
+ if (!string.IsNullOrEmpty(dbName))
+ {
+ baseConnectionString = baseConnectionString.Replace("database=AcaMate", $"database={dbName}");
+ }
+
+ options.UseMySql(baseConnectionString, ServerVersion.AutoDetect(baseConnectionString));
+});
+builder.Services.AddHttpContextAccessor();
+// DB 설정부 끝
-// var currentDirectory = Directory.GetCurrentDirectory();
-// var webRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"
-// ? Path.Combine(currentDirectory, "publish/Debug/wwwroot")
-// : Path.Combine(currentDirectory, "publish/Release/wwwroot");
-var webRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_WEBROOT");
-// builder.WebHost.UseWebRoot(webRootPath);
+var dbString = builder.Configuration.GetConnectionString("MariaDbConnection");
+var userString = builder.Configuration.GetConnectionString("DBAccount");
-var options = new WebApplicationOptions { WebRootPath = webRootPath };
+// JWT 설정부 시작
+builder.Configuration.AddJsonFile("private/jwtSetting.json", optional: true, reloadOnChange: true);
+builder.Services.Configure(builder.Configuration.GetSection("JwtSettings"));
-var builder = WebApplication.CreateBuilder(options);
+builder.Services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get();
+ options.TokenValidationParameters = new TokenValidationParameters {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = jwtSettings.Issuer,
+ ValidAudience = jwtSettings.Audience,
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
+ };
+ });
+// JWT 설정부 끝
+
+builder.Services.AddControllers();
+// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
+// builder.Services.AddScoped(); //
+// builder.Services.AddScoped();
-// var env = builder.Environment.EnvironmentName;
-// string wwwrootPath = env == "Development" ? "/src/publish/Debug/wwwroot" : "/src/publish/Release/wwwroot";
-// builder.WebHost.UseWebRoot(wwwrootPath);
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
+
+// 스웨거 설정 추가 부분
+// builder.Services.AddSwaggerGen();
+builder.Services.AddCustomSwagger();
var app = builder.Build();
-// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
- app.UseSwagger();
- app.UseSwaggerUI();
+ // app.UseSwagger();
+ // app.UseSwaggerUI();
+ app.UseCustomSwaggerUI();
+ app.UseDeveloperExceptionPage(); // 좀더 자세한 예외 정보 제공
+}
+else
+{
+ app.UseExceptionHandler("/error");
+ app.UseHsts();
}
-app.UseExceptionHandler("/Error");
- // .UseStaticFiles()
-app.UseStaticFiles(new StaticFileOptions
- {
- ServeUnknownFileTypes = true
- });
-app.UseRouting();
-app.MapFallbackToFile("index.html");
-var summaries = new[]
-{
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
-};
+app.UseHttpsRedirection();
+app.UseAuthorization();
-app.MapGet("/weatherforecast", () =>
- {
- var forecast = Enumerable.Range(1, 5).Select(index =>
- new WeatherForecast
- (
- DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- Random.Shared.Next(-20, 55),
- summaries[Random.Shared.Next(summaries.Length)]
- ))
- .ToArray();
- return forecast;
- })
- .WithName("GetWeatherForecast")
- .WithOpenApi();
+app.MapControllers();
-app.Run();
-
-record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
-{
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-}
\ No newline at end of file
+app.Run();
\ No newline at end of file
diff --git a/Program/Common/Data/AppDbContext.cs b/Program/Common/Data/AppDbContext.cs
new file mode 100644
index 0000000..d8385aa
--- /dev/null
+++ b/Program/Common/Data/AppDbContext.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore;
+using AcaMate.V1.Models;
+using Version = AcaMate.V1.Models.Version;
+
+namespace AcaMate.Common.Data;
+//database=AcaMate;
+public class AppDbContext: DbContext
+{
+ public AppDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet Versions { get; set; }
+}
\ No newline at end of file
diff --git a/Program/Common/Model/JwtSettings.cs b/Program/Common/Model/JwtSettings.cs
new file mode 100644
index 0000000..7f8d826
--- /dev/null
+++ b/Program/Common/Model/JwtSettings.cs
@@ -0,0 +1,9 @@
+namespace AcaMate.Common.Models;
+
+public class JwtSettings
+{
+ public string SecretKey { get; set; }
+ public string Issuer { get; set; }
+ public string Audience { get; set; }
+ public int ExpiryMinutes { get; set; }
+}
\ No newline at end of file
diff --git a/Program/Common/Model/Status.cs b/Program/Common/Model/Status.cs
new file mode 100644
index 0000000..60f4939
--- /dev/null
+++ b/Program/Common/Model/Status.cs
@@ -0,0 +1,61 @@
+namespace AcaMate.Common.Models;
+
+public class APIResponseStatus
+{
+ public Status Status { get; set; }
+ public T data { get; set; }
+}
+
+public class Status
+{
+ public string Code { get; set; }
+ public string Message { get; set; }
+}
+
+public static class ErrorResponse
+{
+ // private static readonly Lazy _instance = new Lazy();
+ // public static ErrorResponse Instace => _instance.Value;
+
+ // private ErrorResponse()
+ // {
+ // // 외부 초기화 방지
+ // }
+
+ public static readonly APIResponseStatus InvalidInputError = new APIResponseStatus
+ {
+ Status = new Status()
+ {
+ Code = "001",
+ Message = "입력 값이 유효하지 않습니다."
+ }
+ };
+
+ public static readonly APIResponseStatus NotFoundError = new APIResponseStatus
+ {
+ Status = new Status()
+ {
+ Code = "002",
+ Message = "알맞은 값을 찾을 수 없습니다."
+ }
+ };
+
+ public static readonly APIResponseStatus InternalSeverError = new APIResponseStatus
+ {
+ Status = new Status()
+ {
+ Code = "003",
+ Message = "통신에 오류가 발생하였습니다."
+ }
+ };
+
+
+ public static readonly APIResponseStatus UnknownError = new APIResponseStatus
+ {
+ Status = new Status()
+ {
+ Code = "999",
+ Message = "알 수 없는 오류가 발생하였습니다.."
+ }
+ };
+}
diff --git a/Program/V1/Controllers/AppController.cs b/Program/V1/Controllers/AppController.cs
new file mode 100644
index 0000000..25de08f
--- /dev/null
+++ b/Program/V1/Controllers/AppController.cs
@@ -0,0 +1,66 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Text.Json;
+using AcaMate.Common.Data;
+using AcaMate.Common.Models;
+using Microsoft.IdentityModel.Tokens;
+using Version = AcaMate.V1.Models.Version;
+
+namespace AcaMate.V1.Controllers;
+
+[ApiController]
+[Route("/v1/in/app")]
+[ApiExplorerSettings(GroupName = "공통")]
+public class AppController : ControllerBase
+{
+ private readonly AppDbContext _dbContext;
+
+ public AppController(AppDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ [HttpGet("version")]
+ [CustomOperation("앱 버전 확인","앱 버전을 확인해서 업데이트 여부 판단", "시스템")]
+ public IActionResult GetVersionData(string type)
+ {
+ if (string.IsNullOrEmpty(type))
+ {
+ return BadRequest(ErrorResponse.InvalidInputError);
+ }
+
+ try
+ {
+ var version = _dbContext.Versions.FirstOrDefault(v => v.os_type == (type == "I" ? "VO01" : "VO02"));
+
+ if (version == null)
+ {
+ return NotFound(ErrorResponse.NotFoundError);
+ }
+
+ var response = new APIResponseStatus
+ {
+ Status = new Status()
+ {
+ Code = "000",
+ Message = "정상"
+ },
+ data = new Version()
+ {
+ os_type = (version.os_type == "VO01" ? "I" : (version.os_type == "VO02" ? "A" : "W")),
+ final_ver = version.final_ver,
+ force_ver = version.force_ver,
+ dev_ver = version.dev_ver,
+ choice_update_yn = version.choice_update_yn
+ }
+ };
+
+ string jsonString = JsonSerializer.Serialize(response);
+
+ return Ok(jsonString);
+ }
+ catch (Exception ex)
+ {
+ return StatusCode(500, ErrorResponse.UnknownError);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Program/V1/Controllers/ErrorController.cs b/Program/V1/Controllers/ErrorController.cs
new file mode 100644
index 0000000..cb5be62
--- /dev/null
+++ b/Program/V1/Controllers/ErrorController.cs
@@ -0,0 +1,15 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace AcaMate.V1.Controllers;
+
+
+[ApiController]
+[Route("error")]
+public class ErrorController: ControllerBase
+{
+ [HttpGet]
+ public IActionResult HandleError()
+ {
+ return Problem("오류가 발생하였습니다. 잠시후 다시 시도해주세요.");
+ }
+}
\ No newline at end of file
diff --git a/Program/V1/Controllers/MemberController.cs b/Program/V1/Controllers/MemberController.cs
new file mode 100644
index 0000000..00d048a
--- /dev/null
+++ b/Program/V1/Controllers/MemberController.cs
@@ -0,0 +1,29 @@
+
+using System.Text.Json;
+using AcaMate.Common.Data;
+using AcaMate.Common.Models;
+
+using Microsoft.AspNetCore.Http.HttpResults;
+using Microsoft.AspNetCore.Mvc;
+
+namespace AcaMate.V1.Controllers;
+
+[ApiController]
+[Route("v1/in/member")]
+[ApiExplorerSettings(GroupName = "사업자 정보")]
+public class MemberController: ControllerBase
+{
+ [HttpGet("business")]
+ public IActionResult GetBusinessData()
+ {
+ // return Ok("GOOD");
+ return Ok("DB 참조");
+ }
+
+
+ [HttpGet("/v1/out/member/business")]
+ public IActionResult SearchBusinessNo()
+ {
+ return Ok("외부 참조");
+ }
+}
\ No newline at end of file
diff --git a/Program/V1/Controllers/PushController.cs b/Program/V1/Controllers/PushController.cs
new file mode 100644
index 0000000..bdd2e1b
--- /dev/null
+++ b/Program/V1/Controllers/PushController.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Mvc;
+using Swashbuckle.AspNetCore.Annotations;
+
+namespace AcaMate.V1.Controllers;
+
+[ApiController]
+[Route("v1/in/push")]
+[ApiExplorerSettings(GroupName = "공통")]
+public class PushController: ControllerBase
+{
+ [HttpGet()]
+ [CustomOperation("푸시 확인","저장된 양식을 확인 할 수 있다..", "푸시")]
+ public IActionResult GetPushData()
+ {
+ return Ok("SEND");
+ }
+
+ [HttpGet("send")]
+ [CustomOperation("푸시전송","저장된 양식으로, 사용자에게 푸시를 전송한다.", "푸시")]
+ public IActionResult SendPush()
+ {
+ return Ok("SEND");
+ }
+}
\ No newline at end of file
diff --git a/Program/V1/Controllers/UserController.cs b/Program/V1/Controllers/UserController.cs
new file mode 100644
index 0000000..3952323
--- /dev/null
+++ b/Program/V1/Controllers/UserController.cs
@@ -0,0 +1,46 @@
+using System.Text;
+using System.Text.Json;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authentication;
+
+using AcaMate.V1.Services;
+
+namespace AcaMate.V1.Controllers;
+
+
+[ApiController]
+[Route("/v1/in/user")]
+[ApiExplorerSettings(GroupName = "사용자")]
+public class UserController: ControllerBase
+{
+ // private readonly UserController _userController;
+
+ // private readonly UserService _userService;
+ //
+ // public UserController(UserService userService)
+ // {
+ // _userService = userService;
+ // }
+
+ [HttpGet("snsLogin")]
+ public IActionResult SNSLogin()
+ {
+ var response = new
+ {
+ status = new
+ {
+ code = "000",
+ message = "정상"
+ },
+ data = new
+ {
+ uid = "AC0000"
+ }
+ };
+
+ string jsonString = JsonSerializer.Serialize(response);
+
+ return Ok(jsonString);
+ }
+
+}
\ No newline at end of file
diff --git a/Program/V1/Models/Version.cs b/Program/V1/Models/Version.cs
new file mode 100644
index 0000000..ef25105
--- /dev/null
+++ b/Program/V1/Models/Version.cs
@@ -0,0 +1,21 @@
+
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace AcaMate.V1.Models;
+
+[Table("version")]
+public class Version
+{
+ [Key]
+ [MaxLength(4)]
+ public string os_type { get; set; }
+ [MaxLength(8)]
+ public string final_ver { get; set; }
+ [MaxLength(8)]
+ public string dev_ver { get; set; }
+ [MaxLength(8)]
+ public string force_ver { get; set; }
+ public bool choice_update_yn { get; set; }
+
+}
\ No newline at end of file
diff --git a/Program/V1/Services/UserService.cs b/Program/V1/Services/UserService.cs
new file mode 100644
index 0000000..b406693
--- /dev/null
+++ b/Program/V1/Services/UserService.cs
@@ -0,0 +1,6 @@
+namespace AcaMate.V1.Services;
+
+public class UserService
+{
+ // priva
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 9d1c1b6..55ea7fa 100644
--- a/README.md
+++ b/README.md
@@ -4,4 +4,12 @@
### Skill
- .NET Web API
### IDE
-- JetBrains Rider
\ No newline at end of file
+- JetBrains Rider
+
+### 추가 패키지
+| No. | Name | Version | Description |
+|:---:|:-------------------------------------------:|:-------:|:---------------------------|
+| 1 | Microsoft.AspNetCore.OpenApi | 8.0.10 | OpenAPI 를 지원하기 위해 사용 |
+| 2 | Microsoft.EntityFrameworkCore | 8.0.10 | 데이터베이스 작업을 간편하게 수행하기 위해 사용 |
+| 3 | Pomelo.EntityFrameworkCore.MySql | 8.0.2 | MariaDB 연결 |
+| 4 |Microsoft.AspNetCore.Authentication.JwtBearer| 8.0.10 | |
diff --git a/SwaggerConfigure.cs b/SwaggerConfigure.cs
new file mode 100644
index 0000000..6526728
--- /dev/null
+++ b/SwaggerConfigure.cs
@@ -0,0 +1,92 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.Annotations;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+public class CustomOperationAttribute : Attribute
+{
+ public string Summary { get; }
+ public string Description { get; }
+ public string[] Tags { get; }
+
+ public CustomOperationAttribute(string summary, string description, params string[] tags)
+ {
+ Summary = summary;
+ Description = description;
+ Tags = tags;
+ }
+}
+
+
+
+public class CustomSwaggerOperationFilter : IOperationFilter
+{
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ var customSwaggerAttribute = context.MethodInfo.GetCustomAttributes(typeof(CustomOperationAttribute), false)
+ .FirstOrDefault() as CustomOperationAttribute;
+
+ if (customSwaggerAttribute != null)
+ {
+ operation.Summary = customSwaggerAttribute.Summary;
+ operation.Description = customSwaggerAttribute.Description;
+ operation.Tags = customSwaggerAttribute.Tags
+ .Select(tag => new OpenApiTag { Name = tag })
+ .ToList();
+ }
+ }
+}
+
+public static class SwaggerConfigure
+{
+ private static OpenApiInfo DocName(string title, string version)
+ {
+ return new OpenApiInfo
+ {
+ Title = title,
+ Version = version
+ };
+ }
+ public static void AddCustomSwagger(this IServiceCollection services)
+ {
+ services.AddSwaggerGen(options =>
+ {
+ options.EnableAnnotations();
+ options.OperationFilter();
+ options.SwaggerDoc("전체", DocName("AcaMate 전체 API","1.0.0"));
+ options.SwaggerDoc("공통",DocName("공통 API", "1.0.0"));
+ options.SwaggerDoc("사업자 정보", DocName("사업자 정보 API", "1.0.0"));
+ options.SwaggerDoc("사용자", DocName("사용자 API", "1.0.0"));
+
+ options.DocInclusionPredicate((docName, apiDesc) =>
+ {
+ if (docName == "전체") return true; // 전체 문서에 모든 API 포함
+ if (docName == "공통" && apiDesc.GroupName == "공통") return true;
+ if (docName == "사업자 정보" && apiDesc.GroupName == "사업자 정보") return true;
+ if (docName == "사용자" && apiDesc.GroupName == "사용자") return true;
+ return false;
+ });
+
+ options.TagActionsBy(apiDesc => new[] { apiDesc.GroupName ?? "기타" });
+
+
+ // options.TagActionsBy(apiDesc => apiDesc.ActionDescriptor.EndpointMetadata
+ // .OfType()
+ // .FirstOrDefault()?.Tags ?? new[] { "기타" });
+ });
+ }
+
+ public static void UseCustomSwaggerUI(this IApplicationBuilder app)
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI(options =>
+ {
+ options.SwaggerEndpoint("/swagger/전체/swagger.json", "전체 API");
+ options.SwaggerEndpoint("/swagger/공통/swagger.json", "공통 API");
+ options.SwaggerEndpoint("/swagger/사용자/swagger.json", "사용자 API");
+ options.SwaggerEndpoint("/swagger/사업자 정보/swagger.json", "사업자 정보 API");
+ });
+ }
+}
\ No newline at end of file