# 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** | 9 | 9 | 0 | Phase 2-1 + 3-2 ✅ | | **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** | **57** | **8** | - | --- ## 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 ```