refactor: 클린 아키텍쳐 적용 및 프로젝트 분리, 문서 작성 (Closes #5) #6

Merged
seonkyu.kim merged 3 commits from refactor/#5-apply-architecture into develop 2026-01-21 08:17:48 +00:00
13 changed files with 139 additions and 4 deletions
Showing only changes of commit f02a3884a2 - Show all commits

2
Jenkinsfile vendored
View File

@ -21,8 +21,6 @@ pipeline {
TARGET_BUILD_CONTAINER = 'spms-back-build-release'
TARGET_RUN_CONTAINER = 'spms-run-release'
echo ">>> [PROD Mode] SET Release Container"
} else {
error "This branch(${branchName}) can't set container (USE develop or main branch)"
}
else if (branchName == 'staging') {
TARGET_BUILD_CONTAINER = 'spms-back-build-staging'

View File

@ -0,0 +1,54 @@
## 기술 스택
| 기술 | 스택 |
|:------------------|:------------------------|
| Framework | .NET 9.0 / ASP.NET Core |
| Database | MariaDB |
| ORM | Entity Framework Core |
| API Documentation | Swagger |
| Logging | Serilog |
## 프로젝트 구조
프로젝트는 다음과 같은 구조로 구성되어 있다.
### Clean Architecture
- 프로젝트는 클린 아키텍처(Clean Architecture)를 기반으로 설계한다.
- 의존성 방향은 **항상 `외부`에서 `내부`** 로 향한다.
- 외부 : DB, UI, API 등
- 내부 : 비즈니스 로직, 도메인, 애플리케이션 서비스 등
-
### 폴더 구조
```plaintext
SPMS.Server/
├── 1.Domain/ # 순수 비즈니스 영역 : 외부 의존성 절대 금지!
│ ├── Entities/ # DB 테이블과 매핑될 핵심 객체 (Service, Device, Log...)
│ ├── Enums/ # OsType, SendStatus, TargetType 등
│ ├── Constants/ # 시스템 전역 상수 (에러 메시지 등)
│ └── Exceptions/ # 비즈니스 로직 전용 예외 (BusinessException)
├── 2.Application/ # [중재자] 흐름 제어 및 인터페이스 정의
│ ├── Common/ # 공통 응답 포맷 (ResultWrapper, PagedResult)
│ ├── DTOs/ # Request/Response 모델 (서비스 간 데이터 전달용)
│ ├── Interfaces/ # 구현체가 없는 설계도 모음
│ │ ├── Services/ # IServiceService, IPushService
│ │ ├── Repositories/ # IServiceRepository (DB 접근용 인터페이스)
│ │ └── Infra/ # ICryptoHelper, IFcmClient (외부 기술 인터페이스)
│ └── Services/ # 비즈니스 로직 구현체 (ServiceService, PushService)
│ # 주의: 여기서는 DB나 암호화 라이브러리를 직접 쓰지 않고 '인터페이스'만 주입받아 씀
├── 3.Infrastructure/ # 실제 기술 구현 (DB, 암호화, FCM)
│ ├── Persistence/ # DB 관련
│ │ ├── Context/ # AppDbContext (EF Core 설정)
│ │ └── Repositories/ # ServiceRepository (실제 쿼리 수행)
│ ├── Security/ # 암호화/인증 구현 (AesHelper, RsaHelper, JwtGenerator)
│ └── External/ # 외부 시스템 연동 (FcmClient, ApnsClient)
├── 4.Presentation/ # 외부와 연결
│ ├── Controllers/ # API 엔드포인트
│ ├── Middlewares/ # GlobalExceptionHandling, Logging
│ └── Filters/ # 권한 체크 등 (AuthFilter)
├── appsettings.json
├── appsettings.Development.json
└── Program.cs
```아

View File

@ -1,3 +1,5 @@
using Microsoft.EntityFrameworkCore;
using SPMS.Infrastructure;
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
@ -6,10 +8,15 @@ var builder = WebApplication.CreateBuilder(new WebApplicationOptions
?? "wwwroot"
});
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
var app = builder.Build();
// Configure the HTTP request pipeline.

View File

@ -24,4 +24,9 @@
<Folder Include="CheckList\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SPMS.Application\SPMS.Application.csproj" />
<ProjectReference Include="..\SPMS.Infrastructure\SPMS.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SPMS.Domain\SPMS.Domain.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore; // 👈 이거 없으면 DbContext에서 빨간 줄 뜹니다!
namespace SPMS.Infrastructure;
public class AppDbContext : DbContext
{
// 생성자: API 프로젝트에서 옵션(DB 연결 문자열 등)을 주입받기 위함
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
// 나중에 여기에 DbSet<User> Users { get; set; } 같은 테이블들이 추가될 겁니다.
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SPMS.Application\SPMS.Application.csproj" />
<ProjectReference Include="..\SPMS.Domain\SPMS.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPMS_API", "SPMS_API\SPMS_API.csproj", "{B198CE2E-92BC-4197-9E91-B5D5708FC426}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPMS.API", "SPMS.API\SPMS.API.csproj", "{B198CE2E-92BC-4197-9E91-B5D5708FC426}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPMS.Domain", "SPMS.Domain\SPMS.Domain.csproj", "{C7295844-DE6A-4D2A-A113-ADE3D69CC8B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPMS.Application", "SPMS.Application\SPMS.Application.csproj", "{695956AE-67AC-4682-A7E5-2580F3C58AEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SPMS.Infrastructure", "SPMS.Infrastructure\SPMS.Infrastructure.csproj", "{214116B2-1A1F-4B0F-9317-53DA0DFE8FFE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -12,5 +18,17 @@ Global
{B198CE2E-92BC-4197-9E91-B5D5708FC426}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B198CE2E-92BC-4197-9E91-B5D5708FC426}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B198CE2E-92BC-4197-9E91-B5D5708FC426}.Release|Any CPU.Build.0 = Release|Any CPU
{C7295844-DE6A-4D2A-A113-ADE3D69CC8B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7295844-DE6A-4D2A-A113-ADE3D69CC8B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7295844-DE6A-4D2A-A113-ADE3D69CC8B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7295844-DE6A-4D2A-A113-ADE3D69CC8B1}.Release|Any CPU.Build.0 = Release|Any CPU
{695956AE-67AC-4682-A7E5-2580F3C58AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{695956AE-67AC-4682-A7E5-2580F3C58AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{695956AE-67AC-4682-A7E5-2580F3C58AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{695956AE-67AC-4682-A7E5-2580F3C58AEB}.Release|Any CPU.Build.0 = Release|Any CPU
{214116B2-1A1F-4B0F-9317-53DA0DFE8FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{214116B2-1A1F-4B0F-9317-53DA0DFE8FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{214116B2-1A1F-4B0F-9317-53DA0DFE8FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{214116B2-1A1F-4B0F-9317-53DA0DFE8FFE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal