From 01c8f7602f70d39054e592c5beffc6a7d8ca91b1 Mon Sep 17 00:00:00 2001 From: "seonkyu.kim" Date: Tue, 10 Feb 2026 22:38:50 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20TASKS.md=20Phase=203=20=EC=9E=AC?= =?UTF-8?q?=EC=A0=95=EB=B9=84=20-=20API=20Spec=20=EA=B8=B0=EC=A4=80=20(#12?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API_Specification.md 기준으로 Phase 3 테이블 재구성 - 미구현 API 7개 추가 (Message 6.1~6.4, Push 7.3~7.5) - Feature_Spec 전용 항목(딥링크/재발송) 보류 처리 Co-Authored-By: Claude Opus 4.6 --- TASKS.md | 1914 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1914 insertions(+) create mode 100644 TASKS.md diff --git a/TASKS.md b/TASKS.md new file mode 100644 index 0000000..7057ec2 --- /dev/null +++ b/TASKS.md @@ -0,0 +1,1914 @@ +# TASKS.md + +> SPMS API 전체 작업 목록 및 마일스톤 계획 +> +> **📌 기준 문서**: `Documents/API_Specification.md` (65개 API) +> +> Feature_Spec.md, PRD.md는 참고 문서로만 사용. 문서 충돌 시 API_Specification.md 우선. +> +> **⚠️ 1이슈 1완료 원칙**: 이슈 1개씩 순차 진행. 이슈 → 코드 → PR → 머지 완료 후, 다음 이슈는 사용자 승인 받고 시작. 여러 이슈 병렬 진행 금지. + +--- + +## 0. API 커버리지 현황 + +> API_Specification.md 기준 65개 API 구현 현황 + +| 도메인 | API 수 | 구현 완료 | 미구현 | Phase | +|--------|:------:|:---------:|:------:|-------| +| **Public** | 10 | 10 | 0 | Phase 2-2 ✅ | +| **Auth** | 6 | 6 | 0 | Phase 2-1 ✅ | +| **Account** | 5 | 5 | 0 | Phase 2-1 ✅ | +| **Service** | 13 | 13 | 0 | Phase 2-1 ✅ | +| **Device** | 7 | 7 | 0 | Phase 2-2 ✅ | +| **Message** | 5 | 1 | 4 | Phase 3 ← 다음 | +| **Push** | 8 | 5 | 3 | Phase 3 | +| **Stats** | 5 | 0 | 5 | Phase 3-2 | +| **File** | 6 | 6 | 0 | Phase 2-2 ✅ | +| **총계** | **65** | **53** | **12** | - | + +--- + +## 1. 마일스톤 로드맵 + +| 마일스톤 | 버전 | 상태 | +|----------|------|------| +| **Phase 1: 인프라 & 공통 모듈** | v0.1.0 | ✅ 완료 | +| **Phase 2-1: 인증 & 계정 & 서비스 API** | v0.2.0 | ✅ 완료 | +| **Phase 2-2: Public & 디바이스 & 파일 API** | v0.3.0 | ✅ 완료 | +| **Phase 3: 메시지 & Push Core** | v0.4.0 | 🔄 진행 중 | +| **Phase 3-2: 통계 & Webhook & 배치** | v0.5.0 | ⬜ 대기 | +| **MVP: 통합 테스트 & 안정화 & 배포** | v1.0.0 | ⬜ 대기 | +| **Phase 4: Back Office (React)** | v1.1.0 | ⬜ 대기 | +| **Phase 5: SDK & 고급 기능** | v1.2.0 | ⬜ 대기 | + +--- + +## 2. Phase 1: 인프라 & 공통 모듈 + +> **목표**: 모든 도메인 API 구현의 기반이 되는 공통 모듈, 인증, 인프라를 구축한다. +> +> **선행 완료 항목**: +> - [x] Docker Compose 인프라 (MariaDB, RabbitMQ, Redis) +> - [x] Clean Architecture 4-Layer 프로젝트 분리 (Issue #5) +> - [x] NuGet 패키지 설정 (EF Core, Serilog, BCrypt, FirebaseAdmin, RabbitMQ.Client) + +### Issue #1 — Domain Entity + EF Core Configuration + Migration + +``` +제목: [Feature] Domain Entity 정의 및 DB 스키마 구축 +Labels: Type/Feature, Priority/Urgent, Status/Available +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#8-domain-entities-db +Gitea Issue: #8 +``` + +**상태**: ✅ 완료 (PR #9 머지됨) + +**설명**: DB_Schema.md 기반 12개 Entity 클래스를 정의하고, EF Core Fluent API 설정 후 초기 Migration으로 DB 테이블을 생성한다. + +**체크리스트 — Domain Entity (SPMS.Domain)**: +- [x] `SPMS.Domain/Entities/` 디렉토리 생성 +- [x] `BaseEntity.cs` — 공통 필드 (Id) +- [x] `Service.cs` — 서비스 정보 (21 컬럼) +- [x] `ServiceIp.cs` — IP 화이트리스트 (3 컬럼) +- [x] `Admin.cs` — 관리자 정보 (13 컬럼) +- [x] `Device.cs` — 기기 정보 (15 컬럼) +- [x] `Message.cs` — 메시지 내용 (12 컬럼) +- [x] `FileEntity.cs` — 업로드 파일 (9 컬럼) +- [x] `PushSendLog.cs` — 발송 로그 (7 컬럼) +- [x] `PushOpenLog.cs` — 오픈 로그 (5 컬럼) +- [x] `DailyStat.cs` — 일별 통계 (8 컬럼) +- [x] `WebhookLog.cs` — 웹훅 로그 (9 컬럼) +- [x] `SystemLog.cs` — 시스템 로그 (9 컬럼) +- [x] `Payment.cs` — 결제 이력 (12 컬럼) + +**체크리스트 — EF Core Configuration (SPMS.Infrastructure)**: +- [x] `SPMS.Infrastructure/Persistence/Configurations/` 디렉토리 생성 +- [x] 12개 엔티티별 `IEntityTypeConfiguration` 구현 +- [x] `AppDbContext.cs` DbSet 등록 (12개) +- [x] Soft Delete 글로벌 쿼리 필터 적용 (Service, Admin, Message) +- [x] 초기 마이그레이션 생성 (`dotnet ef migrations add InitialCreate`) +- [x] DB 적용 (`dotnet ef database update`) → spms_dev에 테이블 생성 확인 + +--- + +### Issue #2 — Domain Enum 및 상수 정의 + +``` +제목: [Feature] Domain Enum 및 상수 정의 +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#10-domain-enums +Gitea Issue: #10 +``` + +**상태**: ✅ 완료 (PR #11 머지됨) + +**설명**: 도메인 전반에서 사용할 Enum과 상수를 정의한다. + + +**체크리스트**: +- [x] `SPMS.Domain/Enums/` 디렉토리 생성 +- [x] `Platform.cs` — iOS(0), Android(1), Web(2) +- [x] `ServiceStatus.cs` — Active(0), Suspended(1) +- [x] `SubTier.cs` — Free(0), Basic(1), Pro(2) +- [x] `AdminRole.cs` — Super(0), Manager(1), User(2) +- [x] `DeviceStatus.cs` — Active(0), Inactive(1), Blocked(2) +- [x] `MessageStatus.cs` — Draft(0), Pending(1), Sending(2), Sent(3), Failed(4), Cancelled(5) +- [x] `PushResult.cs` — Success(0), Failed(1) +- [x] `PaymentStatus.cs` — Completed(0), Cancelled(1), Refunded(2) +- [x] `TargetType.cs` — All(0), Filter(1), CsvFile(2), UserList(3) +- [x] `LinkType.cs` — App(0), Web(1), DeepLink(2) +- [x] `WebhookEvent.cs` — PushSent(0), PushFailed(1), PushClicked(2) +- [x] `WebhookStatus.cs` — Success(0), Failed(1) +- [x] `ErrorCodes.cs` — Error_Codes.md 기반 3자리 에러 코드 상수 +- [x] Entity `byte` 필드 → Enum 타입 변경 (Service, Admin, Device, PushSendLog, WebhookLog, Payment) +- [x] EF Core Migration 적용 (WebhookLog.EventType: varchar→tinyint) +- [x] 빌드 성공 확인 + +--- + +### Issue #3 — Domain Interface 정의 + +``` +제목: [Feature] Domain Interface 정의 (Repository, Service) +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#12-domain-interfaces +Gitea Issue: #12 +``` + +**상태**: ✅ 완료 (PR #13 머지됨) + +**설명**: Clean Architecture 계층 참조 규칙에 따라 Domain 레이어에 Repository 및 Service 인터페이스를 정의한다. + +**체크리스트**: +- [x] `SPMS.Domain/Interfaces/` 디렉토리 생성 +- [x] `IRepository.cs` — Generic CRUD 인터페이스 (GetById, GetAll, Find, GetPaged, Count, Exists, Add, Update, Delete) +- [x] `IUnitOfWork.cs` — 트랜잭션 관리 (SaveChanges, BeginTransaction, Commit, Rollback) +- [x] `IServiceRepository.cs` — 서비스 전용 쿼리 (ByServiceCode, ByApiKey, WithIps, ByStatus) +- [x] `IAdminRepository.cs` — 관리자 전용 쿼리 (ByEmail, ByAdminCode, EmailExists) +- [x] `IDeviceRepository.cs` — 디바이스 전용 쿼리 (ByServiceAndToken, ActiveCount, ByPlatform) +- [x] `IMessageRepository.cs` — 메시지 전용 쿼리 (ByMessageCode) +- [x] `IPushLogRepository.cs` — 발송 로그 전용 쿼리 (ByMessageId, Failed, BatchAdd, CountByStatus) +- [x] `IFileRepository.cs` — 파일 전용 쿼리 (ByFileName, FileExists) +- [x] `IStatRepository.cs` — 통계 전용 쿼리 (ByServiceAndDate, ByDateRange) +- [x] 빌드 성공 확인 + +--- + +### Issue #4 — 공통 응답 포맷 및 에러 코드 구현 + +``` +제목: [Feature] ApiResponse 공통 응답 포맷 구현 +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#14-api-response +Gitea Issue: #14 +``` + +**상태**: ✅ 완료 (PR #15 머지됨) + +**설명**: 모든 API에서 사용할 통일된 응답 포맷과 에러 코드 체계를 구현한다. + +**체크리스트**: +- [x] `SPMS.Domain/Common/ApiResponse.cs` — 공통 응답 래퍼 (result, code, msg, data) +- [x] `ApiResponse.Success(data)` 정적 팩토리 메서드 +- [x] `ApiResponse.Fail(code, msg)` 정적 팩토리 메서드 +- [x] `SPMS.Domain/Common/ErrorCodes.cs` — 에러 코드 상수 클래스 (Issue #10에서 구현 완료) +- [x] Error_Codes.md 기반 전체 에러 코드 매핑 (Issue #10에서 구현 완료) +- [x] 빌드 성공 확인 + +--- + +### Issue #5 — SpmsException 및 글로벌 예외 처리 미들웨어 + +``` +제목: [Feature] SpmsException 및 글로벌 예외 처리 미들웨어 +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#16-exception-handling +Gitea Issue: #16 +``` + +**상태**: ✅ 완료 (PR #17 머지됨) + +**설명**: 비즈니스 예외 클래스와 모든 예외를 ApiResponse 형태로 변환하는 글로벌 미들웨어를 구현한다. + +> **📌 참조**: `Documents/ProgramSetup.md` §7 (미들웨어 파이프라인 순서) 및 §8.1 (ExceptionMiddleware 구현 가이드) 필독 + +**체크리스트**: +- [x] `SPMS.Domain/Exceptions/SpmsException.cs` — 커스텀 비즈니스 예외 + - ErrorCode, Message 프로퍼티 + - HTTP 상태 코드 매핑 +- [x] `SPMS.API/Middlewares/ExceptionMiddleware.cs` + - SpmsException → ApiResponse.Fail() 변환 + - 예상치 못한 예외 → 104 (서버 내부 오류) 변환 + - Serilog 로깅 연동 +- [x] Program.cs에 미들웨어 등록 (파이프라인 순서 1번) +- [x] 빌드 성공 확인 + +--- + +### Issue #6 — Generic Repository 및 UnitOfWork 구현 + +``` +제목: [Feature] Generic Repository 및 UnitOfWork 패턴 구현 +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#18-repository-pattern +Gitea Issue: #18 +``` + +**상태**: ✅ 완료 (PR #19 머지됨) + +**설명**: Infrastructure 레이어에 Generic Repository와 UnitOfWork 패턴을 구현한다. + +**체크리스트**: +- [x] `SPMS.Infrastructure/Persistence/Repositories/Repository.cs` — IRepository 구현 + - GetByIdAsync, GetAllAsync, FindAsync + - AddAsync, Update, Delete (Soft Delete) + - 페이징 지원 (Skip/Take) +- [x] `SPMS.Infrastructure/Persistence/UnitOfWork.cs` — IUnitOfWork 구현 + - SaveChangesAsync, BeginTransactionAsync +- [x] DI 등록 (AddScoped) +- [x] 빌드 성공 확인 + +--- + +### Issue #7 — JWT 인증 모듈 구현 + +``` +제목: [Feature] JWT 인증 모듈 구현 (Access + Refresh Token) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#20-jwt-auth +Gitea Issue: #20 +``` + +**상태**: ✅ 완료 (PR #21 머지됨) + +**설명**: JWT 기반 인증 체계를 구현한다. Access Token과 Refresh Token을 지원한다. + +> **📌 참조**: `Documents/ProgramSetup.md` §6 (JWT 인증/인가 설정) 및 §7 (파이프라인 순서 10~11번) 필독 + +**체크리스트**: +- [x] `SPMS.Application/Interfaces/IJwtService.cs` — JWT 서비스 인터페이스 +- [x] `SPMS.Application/Settings/JwtSettings.cs` — JWT 설정 POCO (Options Pattern) +- [x] `SPMS.Infrastructure/Auth/JwtService.cs` — JWT 생성/검증 구현 + - Access Token 생성 (Claim: AdminId, Role, ServiceCode) + - Access Token 검증 + - Refresh Token 생성 (랜덤 문자열) +- [x] `SPMS.API/Extensions/AuthenticationExtensions.cs` — AddJwtAuthentication, AddAuthorizationPolicies 확장 메서드 +- [x] appsettings JwtSettings 바인딩 (SecretKey, Issuer, Audience, ExpiryMinutes, RefreshTokenExpiryDays) +- [x] Program.cs에 Authentication/Authorization 서비스 등록 (파이프라인 순서 10~11번) +- [x] NuGet 패키지 추가 (System.IdentityModel.Tokens.Jwt, Microsoft.AspNetCore.Authentication.JwtBearer) +- [x] `[Authorize]`, `[AllowAnonymous]` 어트리뷰트 적용 준비 +- [x] 빌드 성공 확인 + +--- + +### Issue #8 — Serilog 구조적 로깅 설정 + +``` +제목: [Feature] Serilog 구조적 로깅 설정 +Labels: Type/Feature, Priority/Medium, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#22-serilog-logging +Gitea Issue: #22 +``` + +**상태**: ✅ 완료 (PR #23 머지됨) + +**설명**: Serilog를 활용한 구조적 로깅을 설정한다. + +> **📌 참조**: `Documents/ProgramSetup.md` §7 (파이프라인 순서 3~4번) 및 §8.3 (RequestIdMiddleware) 필독 + +**체크리스트**: +- [x] appsettings.json Serilog 섹션 설정 + - Console Sink (Development) + - File Sink (Rolling, 일별) +- [x] `SPMS.API/Middlewares/RequestIdMiddleware.cs` — X-Request-ID 헤더 발급/반환 +- [x] `UseSerilogRequestLogging()` 내장 미들웨어 등록 (파이프라인 순서 4번) +- [x] Program.cs에 Serilog 호스트 빌더 설정 +- [x] 환경별 로그 레벨 분리 (Development: Debug, Production: Warning) + +--- + +### Issue #9 — Health Check 엔드포인트 구현 + +``` +제목: [Feature] Health Check 엔드포인트 구현 +Labels: Type/Feature, Priority/Medium, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#24-health-check +Gitea Issue: #24 +``` + +**상태**: ✅ 완료 (PR #25 머지됨) + +**설명**: 시스템 주요 연동 구간의 상태를 확인하는 Health Check API를 구현한다. (HCK-01) + +**체크리스트**: +- [x] `SPMS.API/Controllers/PublicController.cs` 생성 +- [x] `POST /v1/out/health` 엔드포인트 + - MariaDB 연결 확인 (SELECT 1) + - Redis PING 확인 (미설정 시 스킵) + - RabbitMQ 연결 확인 (미설정 시 스킵) +- [x] 정상: HTTP 200 + ApiResponse.Success +- [x] 이상: HTTP 503 + 상세 상태 JSON + +--- + +### Issue #10 — DI 컨테이너 및 서비스 등록 구조화 + +``` +제목: [Feature] DI 컨테이너 및 서비스 등록 구조화 +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#26-di-setup +Gitea Issue: #26 +``` + +**상태**: ✅ 완료 (PR #27 머지됨) + +**설명**: Program.cs의 DI 등록을 레이어별 확장 메서드로 분리하여 구조화한다. + +> **📌 참조**: `Documents/ProgramSetup.md` §3 (전체 구조), §5 (DI 등록), §7 (UseMiddlewarePipeline) 필독 + +**체크리스트**: +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — AddInfrastructure() 확장 메서드 + - DbContext, Repository, UnitOfWork, JwtService 등록 +- [x] `SPMS.Application/DependencyInjection.cs` — AddApplication() 확장 메서드 + - Application Service 등록 +- [x] `SPMS.API/Extensions/ApplicationBuilderExtensions.cs` — UseMiddlewarePipeline() 확장 메서드 +- [x] Program.cs 정리 + - `builder.Services.AddApplication()` + - `builder.Services.AddInfrastructure(builder.Configuration)` + - `app.UseMiddlewarePipeline()` +- [x] 환경별 설정 바인딩 (ConnectionStrings, JwtSettings, RabbitMQ) + +--- + +### Issue #11 — E2EE 암호화 유틸리티 구현 + +``` +제목: [Feature] E2EE 암호화 유틸리티 구현 (RSA-2048 + AES-256) +Labels: Type/Feature, Priority/High, Status/Done +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#28-e2ee-crypto +Gitea Issue: #28 +``` + +**상태**: ✅ 완료 (PR #29 머지됨) + +**설명**: API 키, 인증서 등 민감 데이터 암호화를 위한 유틸리티와 E2EE 요청/응답 처리를 구현한다. (ENC-01, ENC-02, ENC-03) + +> **📌 참조**: `Documents/ProgramSetup.md` §7.3 — E2EE는 글로벌 미들웨어가 아닌 `[SecureTransport]` Action Filter로 구현 (보안등급 3 엔드포인트만 적용) + +**체크리스트**: +- [x] `SPMS.Infrastructure/Security/AesEncryption.cs` — AES-256 암호화/복호화 +- [x] `SPMS.Infrastructure/Security/RsaEncryption.cs` — RSA-2048 키 쌍 관리 +- [x] `SPMS.Infrastructure/Security/E2EEService.cs` — 하이브리드 암복호화 + - EncryptedKey 복호화 → AES Key 획득 + - RequestCipher 복호화 → 평문 데이터 + - ResponseCipher 암호화 → 응답 암호문 +- [x] `SPMS.Infrastructure/Security/TimestampValidator.cs` — 타임스탬프 검증 (±30초) +- [x] `SPMS.API/Filters/SecureTransportAttribute.cs` — Action Filter 구현 +- [ ] Unit Test 작성 (Bad Padding 사전 방지) — 테스트 프로젝트 구축 후 진행 + +--- + +### Issue #12 — API Rate Limiting + Swagger UI 구현 + +``` +제목: [Feature] API Rate Limiting + Swagger UI 구현 +Labels: Type/Feature, Priority/Medium, Status/In Progress +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#30-rate-limiting-swagger +Gitea Issue: #30 +``` + +**상태**: ✅ 완료 (PR #31 머지됨) + +**설명**: API 요청 속도 제한 미들웨어와 Swagger UI를 구현한다. (RAT-01) + +> **📌 참조**: `Documents/ProgramSetup.md` §7 (파이프라인 순서 9번, 15번) 필독 + +**체크리스트 — Rate Limiting**: +- [x] ASP.NET Core 내장 Rate Limiting (FixedWindow, IP 기반 분당 100회) +- [x] 한도 초과 시 HTTP 429 + 에러코드 106 반환 (ApiResponse 형태) +- [x] Program.cs에 서비스 등록 및 미들웨어 등록 (파이프라인 순서 9번) + +**체크리스트 — Swagger UI (Swashbuckle)**: +- [x] `SPMS.API/Extensions/SwaggerExtensions.cs` — AddSwaggerDocumentation() 확장 메서드 + - 도메인별 API 문서 그룹 (all, public, auth, account, service, device, message, push, stats, file) + - JWT Bearer 인증 UI (Authorize 버튼) + - `[SwaggerOperation]` 어노테이션 지원 +- [x] `SPMS.API/Filters/SpmsHeaderOperationFilter.cs` — 커스텀 헤더 자동 표시 + - `/v1/in/*` → X-Service-Code 헤더 + - `/v1/in/device/*` → X-API-KEY 헤더 +- [x] `ApplicationBuilderExtensions.cs` — UseSwagger + UseSwaggerUI (개발 환경만) +- [x] `PublicController.cs` — GroupName, SwaggerOperation 어노테이션 적용 +- [x] Swashbuckle.AspNetCore 6.9.0 + Annotations 6.9.0 설치 +- [x] Microsoft.AspNetCore.OpenApi 제거 (Swashbuckle과 충돌) + +--- + +### Issue #13 — 서비스 식별 미들웨어 (X-Service-Code / X-API-KEY) + +``` +제목: [Feature] X-Service-Code / X-API-KEY 서비스 식별 미들웨어 구현 +Labels: Type/Feature, Priority/High, Status/Done +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#32-service-code-middleware +Gitea Issue: #32 +``` + +**상태**: ✅ 완료 (PR #33 머지됨) + +**설명**: 멀티테넌시 지원을 위해 X-Service-Code 헤더 기반 서비스 식별 미들웨어와 X-API-KEY 검증 미들웨어를 구현한다. + +> **📌 참조**: `Documents/ProgramSetup.md` §7 (파이프라인 순서 12~13번) 및 §8.4~8.5 (구현 가이드) 필독 + +**체크리스트**: +- [x] `SPMS.Infrastructure/Persistence/Repositories/ServiceRepository.cs` — IServiceRepository 구현 +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — ServiceRepository DI 등록 +- [x] `SPMS.API/Middlewares/ServiceCodeMiddleware.cs` — X-Service-Code 헤더 검증, DB 조회, 서비스 상태 확인 +- [x] `SPMS.API/Middlewares/ApiKeyMiddleware.cs` — X-API-KEY 검증 (SDK/디바이스 전용) +- [x] `ApplicationBuilderExtensions.cs` — 미들웨어 파이프라인 12~13번 등록 +- [x] 공개 API 경로 예외 처리 (`/v1/out/*`, `/swagger`, `/health`) + +--- + +### Issue #14 — Sandbox 모드 구현 + +``` +제목: [Feature] Sandbox 모드 구현 (X-SPMS-TEST) +Labels: Type/Feature, Priority/Low, Status/Done +Milestone: Phase 1: 인프라 & 공통 모듈 +Branch: feature/#34-sandbox-mode +Gitea Issue: #34 +``` + +**상태**: ✅ 완료 (PR #35 머지됨) + +**설명**: 개발/테스트 환경에서 실제 FCM/APNs 발송 없이 동작하는 샌드박스 모드를 구현한다. (SND-01) + +> **📌 참조**: `Documents/ProgramSetup.md` §7 (파이프라인 순서 14번) 및 §8.6 (SandboxMiddleware) 필독 + +**체크리스트**: +- [x] `SPMS.API/Middlewares/SandboxMiddleware.cs` — X-SPMS-TEST 헤더 감지, HttpContext.Items["IsSandbox"] 플래그 저장 +- [x] `ApplicationBuilderExtensions.cs` — 미들웨어 파이프라인 14번 위치 등록 +- [x] 빌드 성공 확인 +- [ ] 샌드박스 모드 시 실제 발송 스킵 로직 (Service 레이어에서 분기) — Push 구현 시 진행 +- [ ] `[TEST]` 태그 로그 기록 — Push 구현 시 진행 + +--- + +### Phase 1 이슈 우선순위 및 의존 관계 + +``` +[Phase 1-1: Domain + DB] + #1 Entity + EF Core + Migration ──┐ (★ 최우선) + #2 Enum 정의 ─────────────────────┤ + #3 Interface 정의 ────────────────┘ + │ +[Phase 1-2: 공통 모듈] + #4 ApiResponse ──────┐ + #5 Exception 처리 ───┤ + │ │ +[Phase 1-3: 인프라] │ + #6 Repository 구현 ──┤ (depends on #1, #3) + #7 JWT 인증 ─────────┤ + #8 Serilog 로깅 ─────┤ + │ │ +[Phase 1-4: 미들웨어] │ + #10 DI 구조화 ───────┤ (depends on #6, #7) + #9 Health Check ────┤ (depends on #10) + #11 E2EE 암호화 ─────┤ + #12 Rate Limiting ───┤ + #13 Service Code ────┤ (depends on #1) + #14 Sandbox 모드 ────┘ +``` + +**작업 순서 (권장)**: +1. `#1` (Entity + DB 구축) → `#2` → `#3` (Domain Layer 완성) +2. `#4` → `#5` (공통 응답/예외 체계) +3. `#6` (Repository) → `#7`, `#8`, `#11` (병렬 가능) +4. `#10` (DI 통합) +5. `#9`, `#12`, `#13`, `#14` (미들웨어/엔드포인트) + +--- + +## 3. Phase 2-1: 인증 & 계정 & 서비스 API + +> **목표**: Auth, Account, Service 도메인 API를 구현한다. + +| # | 이슈 제목 | Type | Priority | 기능 ID / API ID | 상태 | +|---|-----------|------|----------|------------------|------| +| 1 | [Feature] 관리자 로그인 API | Feature | High | ADM-01 | ✅ | +| 2 | [Feature] 관리자 비밀번호 변경 API | Feature | Medium | ADM-02 | ✅ | +| 3 | [Feature] 세션 자동 만료 체크 | Feature | High | ADM-03 | ✅ | +| 4 | [Feature] 운영자 계정 CRUD API | Feature | Medium | ADM-04~07 | ✅ | +| 5 | [Feature] 감사 로그 조회 API | Feature | Low | LOG-01 | ⬜ | +| 6 | [Feature] 서비스 목록/상태변경 API | Feature | High | SVC-03, SVC-05 | ✅ | +| 7 | [Feature] API Key 발급/재발급 API | Feature | High | SEC-01~02 | ✅ | +| 8 | [Feature] IP 화이트리스트 관리 API | Feature | Medium | SEC-03 | ✅ | +| 9 | [Feature] APNs/FCM 키 등록 및 조회 API | Feature | High | CRT-01~03 | ✅ | +| 10 | [Feature] **서비스 등록 API** | Feature | High | API_SPMS_04_SERVICE_01 | ✅ | +| 11 | [Feature] **서비스 수정 API** | Feature | High | API_SPMS_04_SERVICE_04 | ✅ | +| 12 | [Feature] **회원가입 API** | Feature | High | API_SPMS_02_AUTH_01 | ✅ | +| 13 | [Feature] **이메일 중복 체크 API** | Feature | Medium | API_SPMS_02_AUTH_02 | ✅ | +| 14 | [Feature] **이메일 인증 API** | Feature | Medium | API_SPMS_02_AUTH_03 | ✅ | +| 15 | [Feature] **비밀번호 찾기 요청 API** | Feature | Medium | API_SPMS_03_ACCOUNT_01 | ✅ | +| 16 | [Feature] **비밀번호 재설정 API** | Feature | Medium | API_SPMS_03_ACCOUNT_02 | ✅ | +| 17 | [Feature] **내 정보 조회 API** | Feature | Medium | API_SPMS_03_ACCOUNT_04 | ✅ | +| 18 | [Feature] **내 정보 수정 API** | Feature | Medium | API_SPMS_03_ACCOUNT_05 | ✅ | +| 19 | [Feature] **서비스 삭제 API** | Feature | Medium | API_SPMS_04_SERVICE_05 | ✅ | +| 20 | [Feature] **서비스 태그 목록/수정 API** | Feature | Medium | API_SPMS_04_SERVICE_10~11 | ✅ | +| 21 | [Improvement] **X-Service-Code 미들웨어 경로 제외 수정** | Improvement | High | - | ✅ | +| 22 | [Chore] **DB 스키마 문서 동기화 + Admin EF 설정 보완** | Chore | Medium | - | ✅ | +| 23 | [Improvement] **Message Entity link_type 컬럼 추가** | Improvement | Medium | - | ✅ | + +> **⚠️ 누락 항목 추가 (2026-02-10)**: API_Specification.md 기준으로 TASKS.md에 없던 항목들 보완 +> - 서비스 등록/수정/삭제 API: API_Specification.md §4 Service API 참조 +> - 서비스 태그 API: API_Specification.md §4 SERVICE_10~11 +> - Auth API: signup, email/check, email/verify (API_Specification.md §2 Auth API) +> - Account API: password/forgot, password/reset, profile/info, profile/update (API_Specification.md §3 Account API) + +### Issue #1 — 관리자 로그인 API (ADM-01) + +``` +제목: [Feature] 관리자 로그인 API (ADM-01) +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#36-admin-login +Gitea Issue: #36 +``` + +**상태**: ✅ 완료 (PR #37 머지됨) + +**설명**: JWT 기반 관리자 로그인 API를 구현한다. Access Token과 Refresh Token을 발급한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Auth/LoginRequestDto.cs` — 로그인 요청 DTO +- [x] `SPMS.Application/DTOs/Auth/LoginResponseDto.cs` — 로그인 응답 DTO +- [x] `SPMS.Application/Interfaces/IAuthService.cs` — 인증 서비스 인터페이스 +- [x] `SPMS.Application/Services/AuthService.cs` — 로그인 비즈니스 로직 + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Persistence/Repositories/AdminRepository.cs` — IAdminRepository 구현 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/AuthController.cs` — 인증 컨트롤러 +- [x] `POST /v1/in/auth/login` 엔드포인트 구현 +- [x] Swagger 어노테이션 적용 + +**체크리스트 — 검증**: +- [x] 빌드 성공 확인 +- [ ] Swagger UI 테스트 +- [ ] 에러 케이스 확인 + +--- + +### Issue #2 — 세션 자동 만료 체크 및 토큰 관리 API (ADM-03) + +``` +제목: [Feature] 세션 자동 만료 체크 및 토큰 관리 API (ADM-03) +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#38-session-token-management +Gitea Issue: #38 +``` + +**상태**: ✅ 완료 (PR #39 머지됨) + +**설명**: Refresh Token을 사용한 토큰 갱신 및 로그아웃 API를 구현한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Auth/TokenRefreshRequestDto.cs` — 토큰 갱신 요청 DTO +- [x] `SPMS.Application/DTOs/Auth/TokenRefreshResponseDto.cs` — 토큰 갱신 응답 DTO +- [x] `SPMS.Application/Interfaces/IAuthService.cs` — RefreshTokenAsync, LogoutAsync 추가 +- [x] `SPMS.Application/Services/AuthService.cs` — 토큰 갱신/로그아웃 로직 구현 + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Domain/Interfaces/IAdminRepository.cs` — GetByRefreshTokenAsync 추가 +- [x] `SPMS.Infrastructure/Persistence/Repositories/AdminRepository.cs` — GetByRefreshTokenAsync 구현 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/AuthController.cs` — token/refresh, logout 엔드포인트 +- [x] `POST /v1/in/auth/token/refresh` — 토큰 갱신 (AllowAnonymous) +- [x] `POST /v1/in/auth/logout` — 로그아웃 (Authorize) +- [x] Swagger 어노테이션 적용 + +**체크리스트 — 검증**: +- [x] 빌드 성공 확인 +- [ ] Swagger UI 테스트 +- [ ] 에러 케이스 확인 + +--- + +### Issue #3 — 관리자 비밀번호 변경 API (ADM-02) + +``` +제목: [Feature] 관리자 비밀번호 변경 API (ADM-02) +Labels: Type/Feature, Priority/Medium, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#40-change-password +Gitea Issue: #40 +``` + +**상태**: ✅ 완료 (PR #41 머지됨) + +**설명**: 인증된 관리자가 자신의 비밀번호를 변경할 수 있는 API를 구현한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Auth/ChangePasswordRequestDto.cs` — 비밀번호 변경 요청 DTO +- [x] `SPMS.Application/Interfaces/IAuthService.cs` — ChangePasswordAsync 추가 +- [x] `SPMS.Application/Services/AuthService.cs` — 비밀번호 변경 로직 구현 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/AuthController.cs` — password/change 엔드포인트 +- [x] `POST /v1/in/auth/password/change` — 비밀번호 변경 (Authorize) +- [x] Swagger 어노테이션 적용 + +**체크리스트 — 검증**: +- [x] 빌드 성공 확인 +- [x] 현재 비밀번호 검증 로직 +- [x] 새 비밀번호 BCrypt 해싱 + +--- + +### Issue #4 — 운영자 계정 CRUD API (ADM-04~07) + +``` +제목: [Feature] 운영자 계정 CRUD API (ADM-04~07) +Labels: Type/Feature, Priority/Medium, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#42-account-crud +Gitea Issue: #42 +``` + +**상태**: ✅ 완료 (PR #43 머지됨) + +**설명**: Super Admin이 운영자(Manager/User) 계정을 생성, 조회, 수정, 삭제할 수 있는 API를 구현한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Account/CreateAccountRequestDto.cs` +- [x] `SPMS.Application/DTOs/Account/UpdateAccountRequestDto.cs` +- [x] `SPMS.Application/DTOs/Account/AccountResponseDto.cs` +- [x] `SPMS.Application/DTOs/Account/AccountListRequestDto.cs` +- [x] `SPMS.Application/DTOs/Account/AccountListResponseDto.cs` +- [x] `SPMS.Application/Interfaces/IAccountService.cs` +- [x] `SPMS.Application/Services/AccountService.cs` + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/AccountController.cs` +- [x] `POST /v1/in/account/create` — 운영자 생성 +- [x] `POST /v1/in/account/list` — 운영자 목록 조회 +- [x] `POST /v1/in/account/{adminCode}` — 운영자 상세 조회 +- [x] `POST /v1/in/account/{adminCode}/update` — 운영자 수정 +- [x] `POST /v1/in/account/{adminCode}/delete` — 운영자 삭제 + +**체크리스트 — 검증**: +- [x] Super Admin만 접근 가능 (Role 체크) +- [x] 이메일 중복 검사 +- [x] BCrypt 비밀번호 해싱 +- [x] Soft Delete 처리 +- [x] 빌드 성공 확인 + +--- + +### Issue #10 — 서비스 등록 API (API_SPMS_04_SERVICE_01) + +``` +제목: [Feature] 서비스 등록 API +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#52-service-create +Gitea Issue: #52 +``` + +**상태**: ✅ 완료 (PR #53 머지됨) + +**설명**: 새로운 서비스(프로젝트)를 등록하는 API를 구현한다. API_Specification.md §4 참조. + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Service/CreateServiceRequestDto.cs` +- [x] `SPMS.Application/DTOs/Service/CreateServiceResponseDto.cs` +- [x] `SPMS.Application/Interfaces/IServiceManagementService.cs` — CreateAsync 추가 +- [x] `SPMS.Application/Services/ServiceManagementService.cs` — 서비스 생성 로직 구현 +- [x] `POST /v1/in/service/create` 엔드포인트 +- [x] ServiceCode 자동 생성 (NanoID 8자리) +- [x] API Key 자동 생성 (48자 Base64) +- [x] ServiceName 중복 검사 (에러코드 107) +- [x] 빌드 성공 확인 + +--- + +### Issue #11 — 서비스 수정 API (API_SPMS_04_SERVICE_04) + +``` +제목: [Feature] 서비스 수정 API +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#54-service-update +Gitea Issue: #54 +``` + +**상태**: ✅ 완료 (PR #55 머지됨) + +**설명**: 기존 서비스 정보를 수정하는 API. API_Specification.md §4 참조. + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Service/UpdateServiceRequestDto.cs` +- [x] `SPMS.Application/Interfaces/IServiceManagementService.cs` — UpdateAsync 추가 +- [x] `SPMS.Application/Services/ServiceManagementService.cs` — 수정 로직 구현 +- [x] `POST /v1/in/service/update` 엔드포인트 +- [x] ServiceName 변경 시 중복 검사 +- [x] 변경 사항 없으면 NoChange 에러 +- [x] 빌드 성공 확인 + +--- + +### Issue #12 — 회원가입 API (API_SPMS_02_AUTH_01) ⬜ 누락 + +``` +제목: [Feature] 회원가입 API +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#XX-auth-signup +``` + +**상태**: ⬜ 대기 + +**설명**: 신규 관리자 회원가입 API. API_Specification.md §2.1 참조. + +**체크리스트**: +- [ ] `SPMS.Application/DTOs/Auth/SignupRequestDto.cs` +- [ ] `SPMS.Application/DTOs/Auth/SignupResponseDto.cs` +- [ ] `SPMS.Application/Interfaces/IAuthService.cs` — SignupAsync 추가 +- [ ] `POST /v1/in/auth/signup` 엔드포인트 +- [ ] BCrypt 비밀번호 해싱 +- [ ] 이메일 중복 검사 +- [ ] 빌드 성공 확인 + +--- + +### Issue #13 — 이메일 중복 체크 API (API_SPMS_02_AUTH_02) ⬜ 누락 + +``` +제목: [Feature] 이메일 중복 체크 API +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#XX-auth-email-check +``` + +**상태**: ⬜ 대기 + +**체크리스트**: +- [ ] `POST /v1/in/auth/email/check` 엔드포인트 +- [ ] 이메일 존재 여부 반환 + +--- + +### Issue #14 — 이메일 인증 API (API_SPMS_02_AUTH_03) + +``` +제목: [Feature] 이메일 인증 인프라 및 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#64-email-verify +Gitea Issue: #64 +``` + +**상태**: ✅ 완료 (PR #65 머지됨) + +**체크리스트**: +- [x] `SPMS.Application/Interfaces/ITokenStore.cs` — 토큰 저장소 인터페이스 +- [x] `SPMS.Application/Interfaces/IEmailService.cs` — 이메일 서비스 인터페이스 +- [x] `SPMS.Infrastructure/Services/InMemoryTokenStore.cs` — IMemoryCache 기반 구현 +- [x] `SPMS.Infrastructure/Services/ConsoleEmailService.cs` — 콘솔 로그 기반 구현 +- [x] `SPMS.Application/DTOs/Auth/EmailVerifyRequestDto.cs` — 요청 DTO +- [x] `AuthService.SignupAsync` — 인증 코드 생성/저장/발송 추가 +- [x] `AuthService.VerifyEmailAsync` — 인증 코드 검증 구현 +- [x] `POST /v1/in/auth/email/verify` 엔드포인트 +- [x] DI 등록 (ITokenStore, IEmailService, MemoryCache) + +--- + +### Issue #15+#16 — 비밀번호 찾기/재설정 API (API_SPMS_03_ACCOUNT_01~02) + +``` +제목: [Feature] 비밀번호 찾기/재설정 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#66-password-reset +Gitea Issue: #66 +``` + +**상태**: ✅ 완료 (PR #67 머지됨) + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Account/PasswordForgotRequestDto.cs` — 요청 DTO +- [x] `SPMS.Application/DTOs/Account/PasswordResetRequestDto.cs` — 요청 DTO +- [x] `SPMS.API/Controllers/PasswordController.cs` — 비밀번호 컨트롤러 +- [x] `AuthService.ForgotPasswordAsync` — 재설정 토큰 생성/발송 +- [x] `AuthService.ResetPasswordAsync` — 토큰 검증/비밀번호 변경 +- [x] `POST /v1/in/account/password/forgot` 엔드포인트 +- [x] `POST /v1/in/account/password/reset` 엔드포인트 + +--- + +### Issue #17+#18 — 내 정보 조회/수정 API (API_SPMS_03_ACCOUNT_04~05) + +``` +제목: [Feature] 내 정보 조회/수정 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#62-profile-api +Gitea Issue: #62 +``` + +**상태**: ✅ 완료 (PR #63 머지됨) + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Account/ProfileResponseDto.cs` — 응답 DTO +- [x] `SPMS.Application/DTOs/Account/UpdateProfileRequestDto.cs` — 수정 요청 DTO +- [x] `SPMS.API/Controllers/ProfileController.cs` — 프로필 컨트롤러 ([Authorize]) +- [x] `AuthService.GetProfileAsync` — 프로필 조회 +- [x] `AuthService.UpdateProfileAsync` — 프로필 수정 (name, phone) +- [x] `POST /v1/in/account/profile/info` 엔드포인트 +- [x] `POST /v1/in/account/profile/update` 엔드포인트 + +--- + +### Issue #19 — 서비스 삭제 API (API_SPMS_04_SERVICE_05) + +``` +제목: [Feature] 서비스 삭제 API (SERVICE_05) +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#68-service-delete +Gitea Issue: #68 +PR: #69 +``` + +**상태**: ✅ 완료 (PR #69 머지됨) + +**설명**: 서비스를 삭제(Soft Delete)하는 API를 구현한다. API_Specification.md §4 SERVICE_05 참조. + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Service/DeleteServiceRequestDto.cs` — 요청 DTO 생성 +- [x] `SPMS.Application/Interfaces/IServiceManagementService.cs` — DeleteAsync 추가 +- [x] `SPMS.Application/Services/ServiceManagementService.cs` — 삭제 로직 구현 (Soft Delete) +- [x] `SPMS.API/Controllers/ServiceController.cs` — `POST /v1/in/service/delete` 엔드포인트 +- [x] Soft Delete (IsDeleted, DeletedAt) + Status=Suspended +- [x] 빌드 성공 확인 + +--- + +### Issue #20 — 서비스 태그 목록/수정 API (API_SPMS_04_SERVICE_10~11) + +``` +제목: [Feature] 서비스 태그 목록/수정 API (SERVICE_10~11) +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: feature/#70-service-tags +Gitea Issue: #70 +PR: #71 +``` + +**상태**: ✅ 완료 (PR #71 머지됨) + +**설명**: 서비스의 태그 목록 조회 및 수정 API를 구현한다. API_Specification.md §4 SERVICE_10~11 참조. + +**체크리스트**: +- [x] `SPMS.Application/DTOs/Service/ServiceTagsRequestDto.cs` — 태그 조회 요청 DTO +- [x] `SPMS.Application/DTOs/Service/UpdateServiceTagsRequestDto.cs` — 태그 수정 요청 DTO (최대 10개) +- [x] `SPMS.Application/DTOs/Service/ServiceTagsResponseDto.cs` — 태그 응답 DTO (tag_index + tag_name) +- [x] `SPMS.Application/Interfaces/IServiceManagementService.cs` — GetTagsAsync, UpdateTagsAsync 추가 +- [x] `SPMS.Application/Services/ServiceManagementService.cs` — 태그 JSON 파싱/직렬화 로직 구현 +- [x] `SPMS.API/Controllers/ServiceController.cs` — `POST tags/list`, `POST tags/update` 엔드포인트 +- [x] 빌드 성공 확인 + +--- + +### Issue #21 — DB 스키마 문서 동기화 + Admin EF 설정 보완 ✅ + +``` +제목: [Chore] DB 스키마 문서 동기화 + Admin EF 설정 보완 +Labels: Type/Chore, Priority/Medium, Status/Available +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: chore/#72-db-schema-sync +Gitea Issue: #72 +PR: #73 +``` + +**상태**: ✅ 완료 (PR #73 머지됨) + +**설명**: DB_Schema.md와 실제 코드 간 불일치를 해소한다. + +**불일치 내역**: +1. Admin 테이블: `RefreshToken`, `RefreshTokenExpiresAt` 2개 컬럼이 코드에만 있고 문서에 없음 (13→15 컬럼) +2. AdminConfiguration.cs: 위 2개 컬럼의 명시적 EF 설정이 누락됨 (자동 매핑되지만 명시적 설정 필요) +3. DB_Schema.md 총 테이블 수: "16개"로 표기되어 있으나 실제 구현 12개 + 예정 4개 구분 필요 + +**체크리스트**: +- [x] `Documents/DB_Schema.md` — Admin 테이블에 `refresh_token VARCHAR(255)`, `refresh_token_expires_at DATETIME` 추가 (13→15 컬럼) +- [x] `Documents/DB_Schema.md` — 총 테이블 수 명확화 (구현 12 + 예정 4) +- [x] `AdminConfiguration.cs` — RefreshToken, RefreshTokenExpiresAt 명시적 Fluent API 설정 추가 +- [x] 빌드 성공 확인 + +--- + +### Issue #22 — Message Entity link_type 컬럼 추가 ✅ + +``` +제목: [Improvement] Message Entity link_type 컬럼 추가 +Labels: Type/Improvement, Priority/Medium, Status/In Progress +Milestone: Phase 2-1: 인증 & 계정 & 서비스 API +Branch: improvement/#74-message-linktype +Gitea Issue: #74 +PR: #75 +``` + +**상태**: ✅ 완료 (PR #75 머지됨) + +**설명**: DB_Schema.md에 정의된 `Message.link_type` 컬럼이 Entity/Configuration에 누락되어 있으므로 추가한다. + +**불일치 내역**: +- DB_Schema.md: `link_type VARCHAR(20)` — 링크 유형 (deeplink, web, none) +- Message.cs: LinkType 프로퍼티 없음 +- MessageConfiguration.cs: LinkType 설정 없음 + +**체크리스트**: +- [x] `SPMS.Domain/Entities/Message.cs` — `LinkType` 프로퍼티 추가 +- [x] `SPMS.Infrastructure/Persistence/Configurations/MessageConfiguration.cs` — LinkType 설정 추가 +- [x] EF Core Migration 생성 (`AddMessageLinkType`) +- [x] DB 적용 (`dotnet ef database update`) +- [x] 빌드 성공 확인 + +--- + +## 4. Phase 2-2: Public & 디바이스 & 파일 API + +> **목표**: Public, Device, File 도메인 API를 구현한다. + +| # | 이슈 제목 | Type | Priority | 기능 ID / API ID | 상태 | +|---|-----------|------|----------|------------------|------| +| 0 | [Feature] **Public Entity + Migration** (Notice, Banner, FAQ, AppConfig) | Feature | High | (선행) | ✅ | +| 1 | [Feature] 공지사항 목록/상세 API | Feature | Low | PUBLIC_01~02 | ✅ | +| 2 | [Feature] 배너 목록 API | Feature | Low | PUBLIC_03 | ✅ | +| 3 | [Feature] FAQ 목록 API | Feature | Low | PUBLIC_04 | ✅ | +| 4 | [Feature] 이용약관/개인정보처리방침 API | Feature | Low | PUBLIC_05~06 | ✅ | +| 5 | [Feature] 앱 버전 체크 API | Feature | Medium | PUBLIC_07 | ✅ | +| 6 | [Feature] 앱 기본 설정 API | Feature | Medium | PUBLIC_08 | ✅ | +| 6-1 | [Bug] Public API X-Service-Code 의존성 제거 | Bug | High | PUBLIC_01~08 | ✅ | +| 7 | [Feature] 점검 안내 API | Feature | Low | PUBLIC_10 | ✅ | +| 8 | [Feature] 디바이스 CRUD + 목록 API | Feature | High | DEVICE_01~04, 07 | ✅ | +| 9 | [Feature] 디바이스 태그/동의 설정 API | Feature | Medium | DEVICE_05~06 | ✅ | +| 10 | [Feature] 파일 업로드/조회/삭제 API | Feature | Medium | FILE_01~04 | ✅ | +| 11 | [Feature] CSV 검증/템플릿 다운로드 API | Feature | Medium | FILE_05~06 | ✅ | + +> **⚠️ Phase 2-2 변경 이력 (2026-02-10)**: +> - **추가**: #0 Public Entity + Migration (선행 이슈) — Notice, Banner, FAQ, AppConfig 엔티티 및 DB 마이그레이션 +> - **삭제**: ~~CAL-01~02 (필터 속성/모수 계산)~~ → Document_Consistency_Analysis.md: FLT 삭제에 따라 제거 +> - **삭제**: ~~FLT-01~03 (필터 그룹 CRUD)~~ → Document_Consistency_Analysis.md: 태그 기반 발송으로 대체 +> - **삭제**: ~~TST-01 (테스터 기기 관리)~~ → Document_Consistency_Analysis.md: Device.is_tester 플래그로 대체 +> - **재구성**: 디바이스 API를 API_Specification.md 기준 2개 이슈로 재분류 (CRUD+목록 / 태그+동의) + +### Phase 2-2 의존관계 + +``` +[Phase 2-2 의존관계] + +#0 Entity + Migration ──┬── #1 공지사항 API + ├── #2 배너 API + ├── #3 FAQ API + ├── #4 약관/정책 API + ├── #5 앱 버전 API + ├── #6 앱 설정 API + └── #7 점검 API + +#8 디바이스 CRUD ────────┬── #9 태그/동의 API + +#10 파일 CRUD ───────────┬── #11 CSV API +``` + +--- + +### Issue #0 — Public Entity + Migration (선행 이슈) + +``` +제목: [Feature] Public API Entity 정의 및 DB 스키마 구축 (Notice, Banner, FAQ, AppConfig) +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 2-2: Public & 디바이스 & 파일 API +Branch: feature/#76-public-entities +Gitea Issue: #76 +PR: #77 +``` + +**상태**: ✅ 완료 (PR #77 머지됨) + +**설명**: DB_Schema.md에 정의된 Public API 관련 4개 테이블(Notice, Banner, FAQ, AppConfig)의 Entity를 생성하고 EF Core Configuration + Migration을 적용한다. Phase 2-2 Public API #1~#7 전체의 선행 작업. + +**체크리스트 — Domain Entity (SPMS.Domain/Entities/)**: +- [x] `Notice.cs` — 공지사항 (10 컬럼: Id, ServiceId, Title, Content, IsPinned, IsActive, CreatedAt, CreatedBy, UpdatedAt, IsDeleted) +- [x] `Banner.cs` — 배너 (12 컬럼: Id, ServiceId, Title, ImageUrl, LinkUrl, LinkType, Position, SortOrder, IsActive, CreatedAt, UpdatedAt, IsDeleted) +- [x] `Faq.cs` — FAQ (9 컬럼: Id, ServiceId, Category, Question, Answer, SortOrder, IsActive, CreatedAt, UpdatedAt) +- [x] `AppConfig.cs` — 앱 설정 (6 컬럼: Id, ServiceId, ConfigKey, ConfigValue, CreatedAt, UpdatedAt) + +**체크리스트 — EF Core Configuration (SPMS.Infrastructure/Persistence/Configurations/)**: +- [x] `NoticeConfiguration.cs` — Soft Delete 글로벌 필터 적용 +- [x] `BannerConfiguration.cs` — Soft Delete 글로벌 필터 적용 +- [x] `FaqConfiguration.cs` +- [x] `AppConfigConfiguration.cs` — (ServiceId, ConfigKey) 복합 인덱스 + +**체크리스트 — AppDbContext + Migration**: +- [x] `AppDbContext.cs`에 DbSet 4개 추가 (Notices, Banners, Faqs, AppConfigs) +- [x] 마이그레이션 생성 (`AddPublicApiTables`) +- [x] DB 적용 및 테이블 생성 확인 +- [x] 빌드 성공 확인 + +> **⚠️ 블로킹**: 이 이슈가 완료되어야 Phase 2-2 Public API #1~#7을 구현할 수 있음 + +--- + +## 5. Phase 3: 메시지 & Push Core + +> **목표**: Message, Push 도메인 API와 RabbitMQ Worker를 구현한다. +> +> **📌 참조 문서**: `Documents/RabbitMQ_Design.md`, `Documents/BatchScheduler_Design.md` + +### Message API (API Spec §6) + +| # | 이슈 제목 | API ID | URI | Priority | 상태 | +|---|-----------|--------|-----|----------|------| +| 1 | [Feature] 메시지 저장 API | 06_01 | `/v1/in/message/save` | High | ⬜ | +| 2 | [Feature] 메시지 목록 조회 API | 06_02 | `/v1/in/message/list` | High | ⬜ | +| 3 | [Feature] 메시지 상세 조회 API | 06_03 | `/v1/in/message/info` | High | ⬜ | +| 4 | [Feature] 메시지 삭제 API | 06_04 | `/v1/in/message/delete` | Medium | ⬜ | +| 5 | [Feature] 메시지 미리보기 API | 06_05 | `/v1/in/message/preview` | Medium | ✅ | + +### Push API (API Spec §7) + +| # | 이슈 제목 | API ID | URI | Priority | 상태 | +|---|-----------|--------|-----|----------|------| +| 6 | [Feature] 단건 발송 API | 07_01 | `/v1/in/push/send` | Urgent | ✅ | +| 7 | [Feature] 태그 발송 API | 07_02 | `/v1/in/push/send/tag` | Urgent | ✅ | +| 8 | [Feature] 대용량 발송 API (CSV) | 07_03 | `/v1/in/push/send/bulk` | Medium | ⬜ | +| 9 | [Feature] 발송 상태 조회 API | 07_04 | `/v1/in/push/job/status` | Medium | ⬜ | +| 10 | [Feature] 발송 취소 API | 07_05 | `/v1/in/push/job/cancel` | Low | ⬜ | +| 11 | [Feature] 발송 로그 조회 API | 07_06 | `/v1/in/push/log` | Medium | ✅ | +| 12 | [Feature] 예약 발송 API | 07_07 | `/v1/in/push/schedule` | High | ✅ | +| 13 | [Feature] 예약 취소 API | 07_08 | `/v1/in/push/schedule/cancel` | High | ✅ | + +### 인프라 (Worker / MQ / Redis) + +| # | 이슈 제목 | Priority | 상태 | +|---|-----------|----------|------| +| 14 | [Feature] RabbitMQ 인프라 설정 | Urgent | ✅ | +| 15 | [Feature] PushWorker 구현 | Urgent | ✅ | +| 16 | [Feature] ScheduleWorker 구현 | High | ✅ | +| 17 | [Feature] FCM 발송 모듈 구현 | Urgent | ✅ | +| 18 | [Feature] APNs 발송 모듈 구현 | Urgent | ✅ | +| 19 | [Feature] Redis 중복 발송 방지 | High | ✅ | + +### 기타 (Feature_Spec 전용 — API Spec 미정의) + +| # | 이슈 제목 | 기능 ID | 비고 | 상태 | +|---|-----------|---------|------|------| +| 20 | [Feature] 메시지 유효성 검사 | HPR-03 | validate 엔드포인트 구현 완료 | ✅ | +| 21 | ~~[Feature] 딥링크 스키마 검증~~ | UTL-02 | API Spec 미정의 → 후순위 | 🔒 보류 | +| 22 | ~~[Feature] 실패건 재발송~~ | ACT-05 | API Spec 미정의 → 후순위 | 🔒 보류 | + +> **⚠️ Phase 3 변경 이력**: +> - **(2026-02-10)** 삭제: ~~TEM-01~04 (템플릿 생성/조회/목록/삭제)~~ → 템플릿 기능 삭제 결정 +> - **(2026-02-10)** API Spec 기준 재정비: 딥링크(UTL-02), 재발송(ACT-05) 보류 / 메시지 CRUD(6.1~6.4), 대용량 발송(7.3~7.5) 추가 + +--- + +### Issue #8 — RabbitMQ 인프라 설정 + +``` +제목: [Feature] RabbitMQ 인프라 설정 (Exchange/Queue) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#102-rabbitmq-infrastructure +Gitea Issue: #102 +``` + +**상태**: ✅ 완료 (PR #103 머지됨) + +**설명**: RabbitMQ Exchange, Queue 선언 및 연결 설정을 구현한다. + +> **📌 참조**: `Documents/RabbitMQ_Design.md` §2~3 (Exchange/Queue 정의), §8 (연결 설정) + +**체크리스트 — 설정**: +- [x] `appsettings.json` RabbitMQ 섹션 추가 + - HostName, Port, UserName, Password + - Exchange: `spms.push.exchange` + - PushQueue: `spms.push.queue` + - ScheduleQueue: `spms.schedule.queue` + - PrefetchCount: 10 + - MessageTtl: 86400000 (24시간) + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Application/Settings/RabbitMQSettings.cs` — 설정 POCO +- [x] `SPMS.Infrastructure/Messaging/RabbitMQConnection.cs` — 연결 관리 + - ConnectionFactory 설정 (AutomaticRecovery, NetworkRecoveryInterval, RequestedHeartbeat) + - IConnection, IChannel 관리 +- [x] `SPMS.Infrastructure/Messaging/RabbitMQInitializer.cs` — Exchange/Queue 선언 + - Exchange: `spms.push.exchange` (Direct, Durable) + - Queue: `spms.push.queue` (Durable, TTL 24시간) + - Queue: `spms.schedule.queue` (Durable, TTL 24시간) + - Binding: `push` → `spms.push.queue`, `schedule` → `spms.schedule.queue` +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — DI 등록 + +**체크리스트 — 메시지 스키마**: +- [x] `SPMS.Application/DTOs/Push/PushMessageDto.cs` — 발송 메시지 스키마 + - message_id, request_id, service_id, send_type + - title, body, image_url, link_url, custom_data + - target (type, value) + - created_by, created_at +- [x] `SPMS.Application/DTOs/Push/ScheduleMessageDto.cs` — 예약 발송 메시지 스키마 + - schedule_id, message_id, service_id, scheduled_at + - push_message (PushMessage 포함) + +**체크리스트 — Producer**: +- [x] `SPMS.Application/Interfaces/IPushQueueService.cs` — 큐 발행 인터페이스 +- [x] `SPMS.Infrastructure/Messaging/PushQueueService.cs` — 큐 발행 구현 + - PublishPushMessage(PushMessage) → `spms.push.queue` + - PublishScheduleMessage(ScheduleMessage) → `spms.schedule.queue` + - DeliveryMode: Persistent + - ContentType: application/json + +--- + +### Issue #9 — PushWorker 구현 + +``` +제목: [Feature] PushWorker 구현 (RabbitMQ Consumer) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#110-push-worker +Gitea Issue: #110 +``` + +**상태**: ✅ 완료 (PR #111 → develop 머지됨) + +**설명**: RabbitMQ `spms.push.queue`에서 메시지를 소비하여 FCM/APNs로 발송하는 BackgroundService Worker를 구현한다. + +> **📌 참조**: `Documents/BatchScheduler_Design.md` §2 (PushWorker), `Documents/RabbitMQ_Design.md` §6.1 (Consumer 설정) + +**체크리스트 — Worker 구현**: +- [x] `SPMS.Infrastructure/Workers/PushWorker.cs` — BackgroundService 상속 + - RabbitMQ Consumer 설정 (Prefetch 10, Auto Ack false) + - 메시지 수신 → JSON 역직렬화 + - Redis 중복 체크 (request_id) + - send_type별 대상 Device 조회 (single, group, broadcast) + - 플랫폼별 분류 (iOS → APNs, Android → FCM) + - 배치 발송 (FcmSender/ApnsSender) + - 결과 처리 (PushSendLog INSERT) + - DailyStat 증분 업데이트 + - ACK 전송 + +**체크리스트 — 에러 처리**: +- [x] 재시도 정책 구현 (x-retry-count 헤더, 최대 3회) +- [x] FCM/APNs 응답별 처리 (ShouldRemoveDevice → Device 비활성화) + +**체크리스트 — 등록**: +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — AddHostedService() 등록 + +--- + +### Issue #10 — ScheduleWorker 구현 + +``` +제목: [Feature] ScheduleWorker 구현 (예약 발송 스케줄러) +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#112-schedule-worker +Gitea Issue: #112 +``` + +**상태**: ✅ 완료 (PR #113 → develop 머지됨) + +**설명**: RabbitMQ `spms.schedule.queue`에서 예약 메시지를 폴링하여, 예약 시간 도래 시 `spms.push.queue`로 전달하는 BackgroundService Worker를 구현한다. + +> **📌 참조**: `Documents/BatchScheduler_Design.md` §3 (ScheduleWorker), `Documents/RabbitMQ_Design.md` §6.2 + +**체크리스트 — Worker 구현**: +- [x] `SPMS.Infrastructure/Workers/ScheduleWorker.cs` — BackgroundService 상속 + - 30초 간격 폴링 (BasicGet 방식) + - ScheduleMessageDto 역직렬화 → scheduled_at 파싱 + - 예약 시간 미도래 → NACK + requeue + - 예약 시간 도래 → push queue에 적재 → ACK + +**체크리스트 — 등록**: +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — AddHostedService() 등록 + +--- + +### Issue #11 — FCM 발송 모듈 구현 + +``` +제목: [Feature] FCM 발송 모듈 구현 (Firebase Cloud Messaging) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#104-fcm-sender +Gitea Issue: #104 +``` + +**상태**: ✅ 완료 (PR #105 머지됨) + +**설명**: Firebase Admin SDK를 사용하여 Android 디바이스에 푸시를 발송하는 모듈을 구현한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/Interfaces/IFcmSender.cs` — FCM 발송 인터페이스 + - SendAsync(deviceToken, title, body, data) → PushResult + - SendBatchAsync(List, title, body, data) → List +- [x] `SPMS.Application/DTOs/Push/PushResultDto.cs` — 발송 결과 DTO + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Push/FcmSender.cs` — Firebase Admin SDK 연동 + - FirebaseApp 초기화 (Service Account JSON) + - HTTP v1 API 사용 + - 배치 발송 (500건 단위) + - 응답 파싱 (success, failure, canonical_ids) +- [x] Service별 Firebase 인스턴스 관리 (멀티테넌시, SHA256 해시 캐싱) + +**체크리스트 — 에러 처리**: +- [x] FCM 응답 코드별 처리 로직 + - Unregistered → Device 삭제 + - InvalidArgument/SenderIdMismatch → Device 삭제 + - Unavailable/Internal/QuotaExceeded → 재시도 + +--- + +### Issue #12 — APNs 발송 모듈 구현 + +``` +제목: [Feature] APNs 발송 모듈 구현 (Apple Push Notification service) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#106-apns-sender +Gitea Issue: #106 +``` + +**상태**: ✅ 완료 (PR #107 → develop 머지됨) + +**설명**: Apple Push Notification service를 사용하여 iOS 디바이스에 푸시를 발송하는 모듈을 구현한다. + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/Interfaces/IApnsSender.cs` — APNs 발송 인터페이스 + - SendAsync(deviceToken, title, body, data) → PushResult + - SendBatchAsync(List, title, body, data) → List + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Push/ApnsSender.cs` — APNs HTTP/2 연동 + - JWT 토큰 생성 (.p8 Private Key, Key ID, Team ID) + - HTTP/2 클라이언트 (IHttpClientFactory, SocketsHttpHandler) + - 배치 발송 (500건 단위, 동시 50건) + - 응답 파싱 (APNs reason) +- [x] Service별 APNs 설정 관리 (Bundle ID, Key ID, Team ID — 인터페이스 파라미터로 전달) + +**체크리스트 — 에러 처리**: +- [x] APNs 응답 코드별 처리 로직 + - 410 Unregistered → Device 삭제 + - 400 BadDeviceToken/DeviceTokenNotForTopic → Device 삭제 + - 503/429 → 재시도 + +--- + +### Issue #13 — Redis 중복 발송 방지 + +``` +제목: [Feature] Redis 중복 발송 방지 구현 +Labels: Type/Feature, Priority/High, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#108-redis-duplicate-check +Gitea Issue: #108 +``` + +**상태**: ✅ 완료 (PR #109 → develop 머지됨) + +**설명**: Redis를 사용하여 중복 발송을 방지하는 로직을 구현한다. + +> **📌 참조**: `Documents/BatchScheduler_Design.md` §2 처리 흐름 [1] Redis 중복 체크 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/Settings/RedisSettings.cs` — Redis 설정 POCO +- [x] `SPMS.Application/Interfaces/IDuplicateChecker.cs` — 중복 체크 인터페이스 + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Caching/RedisConnection.cs` — Redis 연결 관리 + - StackExchange.Redis 사용 + - ConnectionMultiplexer 싱글톤 관리 +- [x] `SPMS.Infrastructure/Caching/DuplicateChecker.cs` — 중복 체크 로직 + - Key 형식: `{InstanceName}duplicate:{request_id}` + - TTL: 24시간 + - SetNX (SET if Not eXists) 사용 + - 이미 존재하면 → 중복으로 판단, 스킵 + - 없으면 → 키 생성 후 처리 진행 + +**체크리스트 — 설정**: +- [x] `appsettings.json` Redis 섹션 +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — DI 등록 +- [x] `SPMS.Infrastructure/SPMS.Infrastructure.csproj` — StackExchange.Redis 2.10.14 추가 + +### Issue #5 — 즉시 발송 요청 API + +``` +제목: [Feature] 즉시 발송 요청 API (단건/태그) +Labels: Type/Feature, Priority/Urgent, Status/In Progress +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#114-push-send-api +Gitea Issue: #114 +``` + +**상태**: ✅ 완료 (PR #115 → develop 머지됨) + +**설명**: 단건 발송(device_id)과 태그 발송(tags) API를 구현한다. + +> **📌 참조**: `Documents/API_Specification.md` §7.1~7.2 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Push/PushSendRequestDto.cs` — 단건 발송 요청 DTO +- [x] `SPMS.Application/DTOs/Push/PushSendTagRequestDto.cs` — 태그 발송 요청 DTO +- [x] `SPMS.Application/DTOs/Push/PushSendResponseDto.cs` — 발송 응답 DTO +- [x] `SPMS.Application/Interfaces/IPushService.cs` — 푸시 서비스 인터페이스 +- [x] `SPMS.Application/Services/PushService.cs` — 푸시 서비스 구현 +- [x] `SPMS.Application/DependencyInjection.cs` — DI 등록 + +**체크리스트 — Domain Layer**: +- [x] `SPMS.Domain/Common/ErrorCodes.cs` — MessageNotFound(151) 추가 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/PushController.cs` — send, send/tag 엔드포인트 + +--- + +### Issue #6 — 예약 발송 등록/취소 API + +``` +제목: [Feature] 예약 발송 등록/취소 API +Labels: Type/Feature, Priority/High, Status/Done +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#116-push-schedule-api +Gitea Issue: #116 +``` + +**상태**: ✅ 완료 (PR #117 → develop 머지됨) + +**설명**: 예약 발송 등록 및 취소 API를 구현한다. 예약 취소는 Redis 기반 취소 플래그 방식. + +> **📌 참조**: `Documents/API_Specification.md` §7.7~7.8 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Push/PushScheduleRequestDto.cs` — 예약 발송 요청 DTO +- [x] `SPMS.Application/DTOs/Push/PushScheduleCancelRequestDto.cs` — 예약 취소 요청 DTO +- [x] `SPMS.Application/DTOs/Push/PushScheduleResponseDto.cs` — 예약 응답 DTO +- [x] `SPMS.Application/Interfaces/IScheduleCancelStore.cs` — 취소 저장소 인터페이스 +- [x] `SPMS.Application/Interfaces/IPushService.cs` — ScheduleAsync, CancelScheduleAsync 추가 +- [x] `SPMS.Application/Services/PushService.cs` — 예약 발송/취소 로직 구현 + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Caching/ScheduleCancelStore.cs` — Redis 기반 취소 플래그 (7일 TTL) +- [x] `SPMS.Infrastructure/Workers/ScheduleWorker.cs` — 취소 체크 로직 추가 +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — IScheduleCancelStore DI 등록 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/PushController.cs` — schedule, schedule/cancel 엔드포인트 + +--- + +### Issue #1 — 메시지 유효성 검사 + +``` +제목: [Feature] 메시지 유효성 검사 서비스 구현 +Labels: Type/Feature, Priority/High, Status/Done +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#118-message-validation +Gitea Issue: #118 +``` + +**상태**: ✅ 완료 (PR #119 → develop 머지됨) + +**설명**: 메시지 필드(title, body, image_url, link_url, link_type, data) 유효성 검사 서비스를 구현한다. + +> **📌 참조**: `Documents/Feature_Spec.md` HPR-03 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Message/MessageValidateRequestDto.cs` — 검증 요청 DTO +- [x] `SPMS.Application/DTOs/Message/MessageValidationResultDto.cs` — 검증 결과 DTO +- [x] `SPMS.Application/Interfaces/IMessageValidationService.cs` — 검증 서비스 인터페이스 +- [x] `SPMS.Application/Services/MessageValidationService.cs` — 검증 로직 구현 +- [x] `SPMS.Application/DependencyInjection.cs` — DI 등록 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/MessageController.cs` — validate 엔드포인트 + +--- + +### Issue #2 — 메시지 미리보기 데이터 생성 API + +``` +제목: [Feature] 메시지 미리보기 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#120-message-preview +Gitea Issue: #120 +``` + +**상태**: ✅ 완료 (PR #121 → develop 머지됨) + +**설명**: 메시지 코드와 변수를 조합하여 미리보기 데이터를 생성하는 API를 구현한다. + +> **📌 참조**: `Documents/API_Specification.md` §6.5 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Message/MessagePreviewRequestDto.cs` — 미리보기 요청 DTO +- [x] `SPMS.Application/DTOs/Message/MessagePreviewResponseDto.cs` — 미리보기 응답 DTO +- [x] `SPMS.Application/Interfaces/IMessageService.cs` — 메시지 서비스 인터페이스 +- [x] `SPMS.Application/Services/MessageService.cs` — 미리보기 로직 구현 +- [x] `SPMS.Application/DependencyInjection.cs` — DI 등록 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/MessageController.cs` — preview 엔드포인트 + +--- + +### Issue #4 — 메시지 이력 불러오기 API + +``` +제목: [Feature] 메시지 이력 불러오기 API (발송 로그 조회) +Labels: Type/Feature, Priority/Medium, Status/Done +Milestone: Phase 3: 메시지 & Push Core +Branch: feature/#122-push-log-api +Gitea Issue: #122 +``` + +**상태**: ✅ 완료 (PR #123 머지됨) + +**설명**: PushSendLog 테이블을 페이지네이션과 필터로 조회하는 발송 로그 조회 API를 구현한다. + +> **📌 참조**: `Documents/API_Specification.md` §7.6, `Documents/Feature_Spec.md` UTL-01 + +**체크리스트 — Domain Layer**: +- [x] `SPMS.Domain/Interfaces/IPushSendLogRepository.cs` — 발송 로그 Repository 인터페이스 + +**체크리스트 — Application Layer**: +- [x] `SPMS.Application/DTOs/Push/PushLogRequestDto.cs` — 조회 요청 DTO +- [x] `SPMS.Application/DTOs/Push/PushLogResponseDto.cs` — 조회 응답 DTO (items + pagination) +- [x] `SPMS.Application/Interfaces/IPushService.cs` — GetLogAsync 추가 +- [x] `SPMS.Application/Services/PushService.cs` — 발송 로그 조회 로직 + +**체크리스트 — Infrastructure Layer**: +- [x] `SPMS.Infrastructure/Persistence/Repositories/PushSendLogRepository.cs` — Include(Message) 기반 페이징/필터링 +- [x] `SPMS.Infrastructure/DependencyInjection.cs` — IPushSendLogRepository DI 등록 + +**체크리스트 — API Layer**: +- [x] `SPMS.API/Controllers/PushController.cs` — log 엔드포인트 + +--- + +### Issue — 메시지 저장 API (6.1) + +``` +제목: [Feature] 메시지 저장 API 구현 +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: 메시지 템플릿을 저장하는 API를 구현한다. 메시지 코드를 자동 생성하고, 변수(`{{변수명}}`)를 포함할 수 있다. + +> **📌 참조**: `Documents/API_Specification.md` §6.1 + +**체크리스트 — Application Layer**: +- [ ] `SPMS.Application/DTOs/Message/MessageSaveRequestDto.cs` — 저장 요청 DTO (title, body, image_url, link_url, link_type, data) +- [ ] `SPMS.Application/DTOs/Message/MessageSaveResponseDto.cs` — 저장 응답 DTO (message_code, created_at) +- [ ] `SPMS.Application/Interfaces/IMessageService.cs` — SaveAsync 추가 +- [ ] `SPMS.Application/Services/MessageService.cs` — 메시지 저장 로직, 메시지 코드 생성 + +**체크리스트 — API Layer**: +- [ ] `SPMS.API/Controllers/MessageController.cs` — save 엔드포인트 + +--- + +### Issue — 메시지 목록 조회 API (6.2) + +``` +제목: [Feature] 메시지 목록 조회 API 구현 +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: 서비스별 메시지 목록을 페이지네이션으로 조회한다. keyword 검색, is_active 필터 지원. + +> **📌 참조**: `Documents/API_Specification.md` §6.2 + +**체크리스트 — Application Layer**: +- [ ] `SPMS.Application/DTOs/Message/MessageListRequestDto.cs` — 목록 요청 DTO (page, size, keyword, is_active) +- [ ] `SPMS.Application/DTOs/Message/MessageListResponseDto.cs` — 목록 응답 DTO (items + pagination) +- [ ] `SPMS.Application/Interfaces/IMessageService.cs` — GetListAsync 추가 +- [ ] `SPMS.Application/Services/MessageService.cs` — 목록 조회 로직 + +**체크리스트 — API Layer**: +- [ ] `SPMS.API/Controllers/MessageController.cs` — list 엔드포인트 + +--- + +### Issue — 메시지 상세 조회 API (6.3) + +``` +제목: [Feature] 메시지 상세 조회 API 구현 +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: 메시지 코드로 메시지 상세 정보를 조회한다. variables 필드에 템플릿 변수 목록을 반환한다. + +> **📌 참조**: `Documents/API_Specification.md` §6.3 + +**체크리스트 — Application Layer**: +- [ ] `SPMS.Application/DTOs/Message/MessageInfoRequestDto.cs` — 상세 요청 DTO (message_code) +- [ ] `SPMS.Application/DTOs/Message/MessageInfoResponseDto.cs` — 상세 응답 DTO (variables 포함) +- [ ] `SPMS.Application/Interfaces/IMessageService.cs` — GetInfoAsync 추가 +- [ ] `SPMS.Application/Services/MessageService.cs` — 상세 조회 로직, 변수 추출 + +**체크리스트 — API Layer**: +- [ ] `SPMS.API/Controllers/MessageController.cs` — info 엔드포인트 + +--- + +### Issue — 메시지 삭제 API (6.4) + +``` +제목: [Feature] 메시지 삭제 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: 메시지를 Soft Delete 한다 (IsDeleted=true, DeletedAt 설정). Hard Delete는 스케줄러에서 30일 후 처리. + +> **📌 참조**: `Documents/API_Specification.md` §6.4 + +**체크리스트 — Application Layer**: +- [ ] `SPMS.Application/DTOs/Message/MessageDeleteRequestDto.cs` — 삭제 요청 DTO (message_code) +- [ ] `SPMS.Application/Interfaces/IMessageService.cs` — DeleteAsync 추가 +- [ ] `SPMS.Application/Services/MessageService.cs` — Soft Delete 로직 + +**체크리스트 — API Layer**: +- [ ] `SPMS.API/Controllers/MessageController.cs` — delete 엔드포인트 + +--- + +### Issue — 대용량 발송 API (7.3) + +``` +제목: [Feature] 대용량 발송 API 구현 (CSV) +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: CSV 파일 기반 대용량 비동기 발송. job_id를 반환하고 백그라운드에서 처리한다. + +> **📌 참조**: `Documents/API_Specification.md` §7.3 + +**체크리스트**: +- [ ] CSV 파싱 로직 (device_id 필수, 나머지 변수) +- [ ] 비동기 Job 처리 구조 설계 +- [ ] `SPMS.API/Controllers/PushController.cs` — send/bulk 엔드포인트 + +--- + +### Issue — 발송 상태 조회 API (7.4) + +``` +제목: [Feature] 발송 상태 조회 API 구현 +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: job_id로 대용량/태그 발송 작업의 진행 상태를 조회한다. + +> **📌 참조**: `Documents/API_Specification.md` §7.4 + +**체크리스트**: +- [ ] Job 상태 관리 구조 (queued/processing/completed/failed/cancelled) +- [ ] `SPMS.API/Controllers/PushController.cs` — job/status 엔드포인트 + +--- + +### Issue — 발송 취소 API (7.5) + +``` +제목: [Feature] 발송 취소 API 구현 +Labels: Type/Feature, Priority/Low, Status/Available +Milestone: Phase 3: 메시지 & Push Core +``` + +**상태**: ⬜ 대기 + +**설명**: 대기 중이거나 처리 중인 대용량 발송 작업을 취소한다. 이미 발송된 건은 취소 불가. + +> **📌 참조**: `Documents/API_Specification.md` §7.5 + +**체크리스트**: +- [ ] Job 취소 로직 (이미 발송된 건 제외) +- [ ] `SPMS.API/Controllers/PushController.cs` — job/cancel 엔드포인트 + +--- + +## 6. Phase 3-2: 통계 & Webhook & 배치 + +> **목표**: Statistics, Webhook, Batch Scheduler를 구현한다. +> +> **📌 참조 문서**: `Documents/BatchScheduler_Design.md` §4~5 (DailyStatWorker, DeadTokenCleanupWorker) + +| # | 이슈 제목 | Type | Priority | 기능 ID | 상태 | +|---|-----------|------|----------|---------|------| +| 1 | [Feature] 대시보드 요약 조회 API | Feature | High | DSH-01 | ⬜ | +| 2 | [Feature] 시간대별 발송 추이 조회 API | Feature | Medium | DSH-02 | ⬜ | +| 3 | [Feature] 플랫폼별 비중 조회 API | Feature | Medium | DSH-03 | ⬜ | +| 4 | [Feature] 메시지별 전환율 조회 API | Feature | Medium | DSH-04 | ⬜ | +| 5 | [Feature] 발송 이력 조회 API | Feature | High | DDN-01 | ⬜ | +| 6 | [Feature] 발송 상세 로그 조회 API | Feature | High | DDL-02 | ⬜ | +| 7 | [Feature] 통계 리포트 다운로드 API | Feature | Medium | EXP-01 | ⬜ | +| 8 | [Feature] 상세 로그 다운로드 API | Feature | Medium | EXP-02 | ⬜ | +| 9 | [Feature] 실패원인 순위 API | Feature | Medium | ANA-01 | ⬜ | +| 10 | [Feature] 웹훅 설정 API | Feature | High | WHK-01 | ⬜ | +| 11 | [Feature] **웹훅 발송 서비스** | Feature | High | WHK-02 | ⬜ | +| 12 | [Feature] **DailyStatWorker 구현** | Feature | Medium | AAG-01 | ⬜ | +| 13 | [Feature] **DeadTokenCleanupWorker 구현** | Feature | Medium | DTK-01 | ⬜ | +| 14 | [Feature] **데이터 보관 주기 관리 배치** | Feature | Medium | RET-01 | ⬜ | +| 15 | [Feature] **Redis 토큰 캐시 관리** | Feature | Medium | - | ⬜ | + +--- + +### Issue #11 — 웹훅 발송 서비스 + +``` +제목: [Feature] 웹훅 발송 서비스 구현 +Labels: Type/Feature, Priority/High, Status/Available +Milestone: Phase 3-2: 통계 & Webhook & 배치 +Branch: feature/#XX-webhook-service +``` + +**상태**: ⬜ 대기 + +**설명**: 푸시 발송 결과, 예약 실행 등 이벤트 발생 시 등록된 Webhook URL로 알림을 전송하는 서비스를 구현한다. + +> **📌 참조**: `Documents/RabbitMQ_Design.md` §7.2 (Webhook 재시도) + +**체크리스트 — Application Layer**: +- [ ] `SPMS.Application/Interfaces/IWebhookService.cs` — 웹훅 인터페이스 + - SendAsync(serviceId, eventType, payload) → WebhookResult +- [ ] `SPMS.Application/DTOs/Webhook/WebhookPayload.cs` — 웹훅 페이로드 + - event_type: `push_result`, `schedule_executed`, `push_failed` + - timestamp, service_code, data + +**체크리스트 — Infrastructure Layer**: +- [ ] `SPMS.Infrastructure/Webhook/WebhookService.cs` — HTTP 전송 구현 + - HttpClient로 POST 요청 + - 타임아웃: 10초 + - 재시도 정책: 3회 (30초 간격) + - 최종 실패 시 WebhookLog 기록 +- [ ] `SPMS.Infrastructure/Persistence/Repositories/WebhookLogRepository.cs` + +**체크리스트 — 이벤트 타입**: +- [ ] `push_result` — 발송 완료 (성공/실패 수) +- [ ] `schedule_executed` — 예약 발송 실행 +- [ ] `push_failed` — 발송 전체 실패 + +--- + +### Issue #12 — DailyStatWorker 구현 + +``` +제목: [Feature] DailyStatWorker 구현 (일별 통계 집계) +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3-2: 통계 & Webhook & 배치 +Branch: feature/#XX-daily-stat-worker +``` + +**상태**: ⬜ 대기 + +**설명**: 매일 00:05 KST에 전날의 발송 결과를 집계하여 DailyStat 테이블에 기록하는 BackgroundService Worker를 구현한다. + +> **📌 참조**: `Documents/BatchScheduler_Design.md` §4 (DailyStatWorker) + +**체크리스트 — Worker 구현**: +- [ ] `SPMS.Infrastructure/Workers/DailyStatWorker.cs` — BackgroundService 상속 + - Cron 스케줄: 매일 00:05 KST + - 대상 날짜 계산 (어제) + - 서비스별 발송 통계 집계: + ```sql + SELECT service_id, + COUNT(*) as sent_cnt, + SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as success_cnt, + SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as fail_cnt + FROM PushSendLog + WHERE DATE(sent_at) = :yesterday + GROUP BY service_id + ``` + - 서비스별 열람 통계 집계: + ```sql + SELECT service_id, COUNT(*) as open_cnt + FROM PushOpenLog + WHERE DATE(opened_at) = :yesterday + GROUP BY service_id + ``` + - DailyStat UPSERT (존재하면 UPDATE, 없으면 INSERT) + - SystemLog에 집계 완료 로그 기록 + +**체크리스트 — 등록**: +- [ ] `Program.cs` — `builder.Services.AddHostedService()` +- [ ] 환경별 활성화 설정 (Debug: 비활성, Staging/Release: 활성) + +--- + +### Issue #13 — DeadTokenCleanupWorker 구현 + +``` +제목: [Feature] DeadTokenCleanupWorker 구현 (비활성 토큰 정리) +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3-2: 통계 & Webhook & 배치 +Branch: feature/#XX-dead-token-cleanup +``` + +**상태**: ⬜ 대기 + +**설명**: 매주 일요일 03:00 KST에 비활성 상태로 7일 이상 경과한 Device 토큰을 물리 삭제하는 BackgroundService Worker를 구현한다. + +> **📌 참조**: `Documents/BatchScheduler_Design.md` §5 (DeadTokenCleanupWorker) + +**체크리스트 — Worker 구현**: +- [ ] `SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs` — BackgroundService 상속 + - Cron 스케줄: 매주 일요일 03:00 KST + - 삭제 대상 조회: + ```sql + SELECT COUNT(*) + FROM Device + WHERE is_active = false + AND updated_at < NOW() - INTERVAL 7 DAY + ``` + - 배치 삭제 (1000건씩): + ```sql + DELETE FROM Device + WHERE is_active = false + AND updated_at < NOW() - INTERVAL 7 DAY + LIMIT 1000 + ``` + - 삭제된 행 > 0 → 반복, = 0 → 완료 + - Redis 토큰 캐시 무효화 (삭제된 Device 기반) + - SystemLog에 정리 완료 로그 + +**체크리스트 — 안전장치**: +- [ ] 배치 단위 삭제 (1000건씩) → DB 부하 분산 +- [ ] 삭제 전 카운트 로깅 +- [ ] 비정상 수치 감지 시 중단 (전체의 50% 이상이면 경고) + +**체크리스트 — 등록**: +- [ ] `Program.cs` — `builder.Services.AddHostedService()` +- [ ] 환경별 활성화 설정 (Debug/Staging: 비활성, Release: 활성) + +--- + +### Issue #14 — 데이터 보관 주기 관리 배치 + +``` +제목: [Feature] 데이터 보관 주기 관리 배치 +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3-2: 통계 & Webhook & 배치 +Branch: feature/#XX-data-retention +``` + +**상태**: ⬜ 대기 + +**설명**: 보관 주기가 지난 로그 데이터를 정리하는 배치 작업을 구현한다. + +**체크리스트**: +- [ ] `SPMS.Infrastructure/Workers/DataRetentionWorker.cs` — BackgroundService 상속 + - Cron 스케줄: 매일 04:00 KST + - PushSendLog: 90일 이전 데이터 삭제 + - PushOpenLog: 90일 이전 데이터 삭제 + - WebhookLog: 30일 이전 데이터 삭제 + - SystemLog: 180일 이전 데이터 삭제 +- [ ] 배치 단위 삭제 (10000건씩) +- [ ] SystemLog에 정리 완료 로그 + +--- + +### Issue #15 — Redis 토큰 캐시 관리 + +``` +제목: [Feature] Redis 토큰 캐시 관리 +Labels: Type/Feature, Priority/Medium, Status/Available +Milestone: Phase 3-2: 통계 & Webhook & 배치 +Branch: feature/#XX-redis-token-cache +``` + +**상태**: ⬜ 대기 + +**설명**: 디바이스 토큰을 Redis에 캐싱하여 DB 조회를 최소화한다. + +> **📌 참조**: MEMORY.md Redis 설정 + +**체크리스트 — Infrastructure Layer**: +- [ ] `SPMS.Infrastructure/Caching/TokenCacheService.cs` — 토큰 캐시 관리 + - Key 형식: `device:token:{serviceId}:{userId}` + - TTL: 1시간 + - GetDeviceTokenAsync(serviceId, userId) → string? + - SetDeviceTokenAsync(serviceId, userId, token) + - InvalidateAsync(serviceId, userId) + - InvalidateByServiceAsync(serviceId) — 서비스 전체 무효화 + +**체크리스트 — PushWorker 연동**: +- [ ] 발송 시 Redis 캐시 우선 조회 +- [ ] 캐시 미스 시 DB 조회 후 캐시 저장 +- [ ] Device 삭제/비활성 시 캐시 무효화 + +--- + +## 7. MVP: 통합 테스트 & 안정화 & 배포 + +> **목표**: 전체 API 통합 테스트, 성능 최적화, 배포 준비를 완료한다. + +| # | 이슈 제목 | Type | Priority | +|---|-----------|------|----------| +| 1 | [Task] 전체 API 통합 테스트 | Task | Urgent | +| 2 | [Improvement] 성능 최적화 및 부하 테스트 | Improvement | High | +| 3 | [Chore] Docker Compose Production 설정 | Chore | High | +| 4 | [Chore] Jenkins 배포 파이프라인 최종 설정 | Chore | High | +| 5 | [Documentation] API 문서 최종 정리 | Documentation | Medium | +| 6 | [Task] v1.0.0 릴리즈 | Task | Urgent | + +--- + +## 8. Phase 4: Back Office (React) + +> **목표**: 관리자 웹 대시보드를 구현한다. (PRD Phase 4) + +| # | 이슈 제목 | Type | Priority | +|---|-----------|------|----------| +| 1 | [Feature] React 프로젝트 초기 설정 | Feature | High | +| 2 | [Feature] 로그인/인증 화면 | Feature | Urgent | +| 3 | [Feature] 대시보드 메인 화면 | Feature | High | +| 4 | [Feature] 메시지 작성 및 발송 UI | Feature | High | +| 5 | [Feature] 서비스/디바이스 관리 화면 | Feature | Medium | +| 6 | [Feature] 통계 및 이력 조회 화면 | Feature | Medium | + +--- + +## 9. Phase 5: SDK & 고급 기능 + +> **목표**: iOS/Android SDK 개발 및 고급 기능을 추가한다. (PRD Phase 5) + +| # | 이슈 제목 | Type | Priority | 기능 ID | +|---|-----------|------|----------|---------| +| 1 | [Feature] iOS SDK 개발 | Feature | High | INI-01~02, REN-01~02, SDK-ACT-01~02, SET-01 | +| 2 | [Feature] Android SDK 개발 | Feature | High | INI-01~02, REN-01~02, SDK-ACT-01~02, SET-01 | +| 3 | [Documentation] SDK 연동 가이드 문서 | Documentation | Medium | - | +| 4 | [Improvement] 부하 테스트 및 성능 최적화 | Improvement | High | - | + +--- + +## 부록: Gitea 이슈 생성 체크리스트 + +모든 이슈 생성 시 아래 항목을 준수한다. (Workflow.md 기준) + +``` +✅ 제목: [타입] 작업 제목 +✅ 본문: 설명 + 체크리스트 +✅ Labels: Type/*, Priority/*, Status/Available +✅ Milestone: Phase X: 설명 +✅ Assignee: seonkyu.kim +``` + +PR 생성 시: + +``` +✅ 제목: 타입: 작업 요약 (#이슈번호) +✅ 본문: 요약 + Closes #n +✅ Labels: Type/*, Priority/*, Status/In Progress +✅ Milestone: Phase X: 설명 +✅ Assignee: seonkyu.kim +✅ Reviewer: Team SPMS/Owners +```