SPMS_API/TASKS.md
2026-02-11 10:10:11 +09:00

1915 lines
76 KiB
Markdown

# 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<T>` 구현
- [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<T>.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<T> 공통 응답 포맷 구현
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<T>.Success(data)` 정적 팩토리 메서드
- [x] `ApiResponse<T>.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<T> 구현
- 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<PushWorker>() 등록
---
### 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<ScheduleWorker>() 등록
---
### 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<deviceToken>, title, body, data) → List<PushResult>
- [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<deviceToken>, title, body, data) → List<PushResult>
**체크리스트 — 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<DailyStatWorker>()`
- [ ] 환경별 활성화 설정 (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<DeadTokenCleanupWorker>()`
- [ ] 환경별 활성화 설정 (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
```