From a6d9f2a46f4c74fa4d4f70127d77cdefb7b4a8f6 Mon Sep 17 00:00:00 2001 From: SEAN Date: Wed, 11 Feb 2026 11:13:26 +0900 Subject: [PATCH] =?UTF-8?q?improvement:=20DeadTokenCleanupWorker=20Redis?= =?UTF-8?q?=20=EC=BA=90=EC=8B=9C=20=EB=AC=B4=ED=9A=A8=ED=99=94=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ITokenCacheService 주입, 배치 삭제 시 Redis 캐시 무효화 - SELECT → DELETE → 캐시 무효화 순서로 변경 - TASKS.md git 트래킹 해제 (.gitignore에 이미 등록됨) Closes #160 --- .../Workers/DeadTokenCleanupWorker.cs | 39 +- TASKS.md | 1913 ----------------- 2 files changed, 29 insertions(+), 1923 deletions(-) delete mode 100644 TASKS.md diff --git a/SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs b/SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs index 6899a74..3bc6f05 100644 --- a/SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs +++ b/SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using SPMS.Application.Interfaces; using SPMS.Domain.Entities; using SPMS.Infrastructure.Persistence; @@ -10,6 +11,7 @@ namespace SPMS.Infrastructure.Workers; public class DeadTokenCleanupWorker : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; + private readonly ITokenCacheService _tokenCache; private readonly ILogger _logger; private static readonly TimeZoneInfo KstZone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Seoul"); @@ -18,9 +20,11 @@ public class DeadTokenCleanupWorker : BackgroundService public DeadTokenCleanupWorker( IServiceScopeFactory scopeFactory, + ITokenCacheService tokenCache, ILogger logger) { _scopeFactory = scopeFactory; + _tokenCache = tokenCache; _logger = logger; } @@ -99,23 +103,38 @@ public class DeadTokenCleanupWorker : BackgroundService return; } - // 배치 삭제 (1000건씩, 트랜잭션 없음) + // 배치 삭제 (1000건씩, 트랜잭션 없음) + Redis 캐시 무효화 var totalDeleted = 0; - int deletedInBatch; - do + while (!stoppingToken.IsCancellationRequested) { - deletedInBatch = await context.Database.ExecuteSqlRawAsync( - "DELETE FROM Device WHERE is_active = false AND updated_at < {0} LIMIT 1000", - new object[] { cutoffUtc }, + var batch = await context.Set() + .Where(d => !d.IsActive && d.UpdatedAt < cutoffUtc) + .Select(d => new { d.Id, d.ServiceId }) + .Take(BatchSize) + .ToListAsync(stoppingToken); + + if (batch.Count == 0) + break; + + var ids = batch.Select(b => b.Id).ToList(); + var idList = string.Join(",", ids); + + #pragma warning disable EF1002 // ID는 내부 DB 쿼리에서 추출한 long 값 — SQL injection 위험 없음 + var deletedInBatch = await context.Database.ExecuteSqlRawAsync( + $"DELETE FROM Device WHERE id IN ({idList})", stoppingToken); + #pragma warning restore EF1002 totalDeleted += deletedInBatch; - if (deletedInBatch > 0) - _logger.LogInformation("DeadTokenCleanupWorker 배치 삭제: {BatchCount}건 (누적: {TotalDeleted}건)", - deletedInBatch, totalDeleted); - } while (deletedInBatch > 0 && !stoppingToken.IsCancellationRequested); + // 삭제된 디바이스의 Redis 캐시 무효화 + foreach (var item in batch) + await _tokenCache.InvalidateAsync(item.ServiceId, item.Id); + + _logger.LogInformation("DeadTokenCleanupWorker 배치 삭제: {BatchCount}건 (누적: {TotalDeleted}건), 캐시 무효화 완료", + deletedInBatch, totalDeleted); + } await WriteSystemLogAsync(context, totalDeleted, stoppingToken); diff --git a/TASKS.md b/TASKS.md deleted file mode 100644 index 7ca84b3..0000000 --- a/TASKS.md +++ /dev/null @@ -1,1913 +0,0 @@ -# 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 | 5 | 0 | Phase 3 ✅ | -| **Push** | 8 | 8 | 0 | Phase 3 ✅ | -| **Stats** | 5 | 5 | 0 | Phase 3-2 ✅ | -| **File** | 6 | 6 | 0 | Phase 2-2 ✅ | -| **총계** | **65** | **65** | **0** | ✅ 전체 구현 완료 | - ---- - -## 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/#150-dead-token-cleanup -Gitea Issue: #150 -``` - -**상태**: ✅ 완료 (PR #151 머지됨) - -**설명**: 매주 일요일 03:00 KST에 비활성 상태로 7일 이상 경과한 Device 토큰을 물리 삭제하는 BackgroundService Worker를 구현한다. - -> **📌 참조**: `Documents/BatchScheduler_Design.md` §5 (DeadTokenCleanupWorker) - -**체크리스트 — Worker 구현**: -- [x] `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 토큰 캐시 무효화 → #15 구현 후 연동 예정 - - SystemLog에 정리 완료 로그 - -**체크리스트 — 안전장치**: -- [x] 배치 단위 삭제 (1000건씩) → DB 부하 분산 -- [x] 삭제 전 카운트 로깅 -- [x] 비정상 수치 감지 시 중단 (전체의 50% 이상이면 경고) - -**체크리스트 — 등록**: -- [x] `SPMS.Infrastructure/DependencyInjection.cs` — `AddHostedService()` -- [ ] 환경별 활성화 설정 (Debug/Staging: 비활성, Release: 활성) → MVP Phase에서 설정 - ---- - -### Issue #14 — 데이터 보관 주기 관리 배치 - -``` -제목: [Feature] 데이터 보관 주기 관리 배치 -Labels: Type/Feature, Priority/Medium, Status/Available -Milestone: Phase 3-2: 통계 & Webhook & 배치 -Branch: feature/#152-data-retention -Gitea Issue: #152 -``` - -**상태**: ✅ 완료 (PR #153 머지됨) - -**설명**: 보관 주기가 지난 로그 데이터를 정리하는 배치 작업을 구현한다. - -**체크리스트**: -- [x] `SPMS.Infrastructure/Workers/DataRetentionWorker.cs` — BackgroundService 상속 - - Cron 스케줄: 매일 04:00 KST - - PushSendLog: 90일 이전 데이터 삭제 - - PushOpenLog: 90일 이전 데이터 삭제 - - WebhookLog: 30일 이전 데이터 삭제 - - SystemLog: 180일 이전 데이터 삭제 -- [x] 배치 단위 삭제 (10000건씩) -- [x] SystemLog에 정리 완료 로그 - ---- - -### Issue #15 — Redis 토큰 캐시 관리 - -``` -제목: [Feature] Redis 토큰 캐시 관리 -Labels: Type/Feature, Priority/Medium, Status/Available -Milestone: Phase 3-2: 통계 & Webhook & 배치 -Branch: feature/#154-redis-token-cache -Gitea Issue: #154 -``` - -**상태**: ✅ 완료 (PR #155 머지됨) - -**설명**: 디바이스 토큰을 Redis에 캐싱하여 DB 조회를 최소화한다. - -**체크리스트 — Infrastructure Layer**: -- [x] `SPMS.Application/Interfaces/ITokenCacheService.cs` — 인터페이스 + CachedDeviceInfo record -- [x] `SPMS.Infrastructure/Caching/TokenCacheService.cs` — Redis 기반 구현 - - Key 형식: `device:token:{serviceId}:{deviceId}` - - TTL: 1시간 - - GetDeviceInfoAsync / SetDeviceInfoAsync / InvalidateAsync / InvalidateByServiceAsync - -**체크리스트 — PushWorker 연동**: -- [x] 발송 시 Redis 캐시 우선 조회 (single 발송) -- [x] 캐시 미스 시 DB 조회 후 캐시 저장 -- [x] 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 -```