ㅑ# Phase 6: 커뮤니케이션 & 통계 **기간:** 1-2주 **우선순위:** 중간 (완성도 및 부가 기능) **의존성:** Phase 1-5 (모든 데이터 활용) ## 📋 Phase 개요 고객 커뮤니케이션 및 데이터 분석 기능을 완성합니다. **포함 기능:** 1. 설정 - 이메일 관리 2. 설정 - 문자 관리 3. 통계 (Statistics & Analytics) --- ## 1️⃣ 설정 - 이메일 관리 ### 기능 목록 #### 1.1 SMTP 설정 - **경로:** `/mng/settings/email/smtp` - **기능:** - SMTP 서버 정보 (호스트, 포트, 암호화) - 인증 정보 (사용자명, 비밀번호) - 발신자 정보 (이름, 이메일) - 테스트 메일 발송 - **권한:** `settings.email.smtp` #### 1.2 이메일 템플릿 관리 - **경로:** `/mng/settings/email/templates` - **기능:** - 템플릿 생성/수정/삭제 - 타입별 템플릿 (회원가입, 비밀번호 재설정, 결제 완료 등) - HTML 에디터 (변수 치환 지원) - 미리보기 - **권한:** `settings.email.templates` #### 1.3 이메일 발송 내역 - **경로:** `/mng/settings/email/history` - **기능:** - 발송 내역 목록 - 검색 (수신자, 제목) - 필터 (상태, 날짜) - 재발송 - 실패 로그 확인 - **권한:** `settings.email.history` #### 1.4 이메일 예약 발송 - **경로:** `/mng/settings/email/scheduled` - **기능:** - 예약 발송 목록 - 새 예약 생성 - 예약 취소 - **권한:** `settings.email.scheduled` ### DB 스키마 ```sql CREATE TABLE email_settings ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL COMMENT 'NULL = 전역 설정', smtp_host VARCHAR(255) NOT NULL, smtp_port INT DEFAULT 587, smtp_encryption ENUM('tls', 'ssl', 'none') DEFAULT 'tls', smtp_username VARCHAR(255) NOT NULL, smtp_password VARCHAR(255) NOT NULL COMMENT '암호화 저장', from_name VARCHAR(255) NOT NULL, from_email VARCHAR(255) NOT NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_tenant_id (tenant_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE email_templates ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL, name VARCHAR(255) NOT NULL, type VARCHAR(100) NOT NULL COMMENT 'welcome, password_reset, payment_success 등', subject VARCHAR(255) NOT NULL, body TEXT NOT NULL COMMENT 'HTML 내용', variables JSON NULL COMMENT '사용 가능한 변수', is_default BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at TIMESTAMP NULL, INDEX idx_tenant_id (tenant_id), INDEX idx_type (type) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE email_logs ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL, template_id BIGINT UNSIGNED NULL, recipient_email VARCHAR(255) NOT NULL, recipient_name VARCHAR(255) NULL, subject VARCHAR(255) NOT NULL, body TEXT NOT NULL, status ENUM('pending', 'sent', 'failed') DEFAULT 'pending', sent_at TIMESTAMP NULL, failed_reason TEXT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_tenant_id (tenant_id), INDEX idx_status (status), INDEX idx_recipient_email (recipient_email), INDEX idx_sent_at (sent_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` ### API 엔드포인트 | Method | Endpoint | Description | FormRequest | |--------|----------|-------------|-------------| | GET | `/mng/settings/email/smtp` | SMTP 설정 조회 | - | | PUT | `/mng/settings/email/smtp` | SMTP 설정 저장 | `UpdateSmtpRequest` | | POST | `/mng/settings/email/smtp/test` | 테스트 메일 발송 | `TestEmailRequest` | | GET | `/mng/settings/email/templates` | 템플릿 목록 | - | | POST | `/mng/settings/email/templates` | 템플릿 생성 | `StoreEmailTemplateRequest` | | PUT | `/mng/settings/email/templates/{id}` | 템플릿 수정 | `UpdateEmailTemplateRequest` | | GET | `/mng/settings/email/history` | 발송 내역 | - | | POST | `/mng/settings/email/send` | 이메일 발송 | `SendEmailRequest` | ### Service 클래스 ```php // app/Services/EmailService.php class EmailService { public function getSettings(int $tenantId = null): ?EmailSetting; public function updateSettings(array $data, int $tenantId = null): EmailSetting; public function testConnection(EmailSetting $setting, string $testEmail): bool; public function listTemplates(int $tenantId = null): Collection; public function createTemplate(array $data): EmailTemplate; public function updateTemplate(EmailTemplate $template, array $data): EmailTemplate; public function render(EmailTemplate $template, array $variables): string; public function send(string $to, string $subject, string $body, int $templateId = null): EmailLog; public function sendBulk(array $recipients, string $subject, string $body): int; public function getHistory(array $filters): LengthAwarePaginator; public function retry(EmailLog $log): bool; } ``` ### 개발 체크리스트 - [ ] `EmailSetting`, `EmailTemplate`, `EmailLog` 모델 작성 - [ ] `EmailService` 클래스 작성 - [ ] Laravel Mail + Queue 설정 - [ ] SMTP 연결 테스트 기능 - [ ] 템플릿 변수 치환 로직 - [ ] 예약 발송 스케줄러 (Laravel Schedule) - [ ] 실패 재시도 로직 - [ ] FormRequest 작성 - [ ] i18n 키 작성 - [ ] 테스트 작성 --- ## 2️⃣ 설정 - 문자 관리 ### 기능 목록 #### 2.1 SMS API 설정 - **경로:** `/mng/settings/sms/api` - **기능:** - API 연동 설정 (알리고, 카카오 알림톡 등) - API 키 관리 - 발신 번호 설정 - 테스트 문자 발송 - **권한:** `settings.sms.api` #### 2.2 문자 템플릿 관리 - **경로:** `/mng/settings/sms/templates` - **기능:** - 템플릿 생성/수정/삭제 - SMS (90자), LMS (2000자) 구분 - 변수 치환 지원 - 바이트 수 자동 계산 - **권한:** `settings.sms.templates` #### 2.3 문자 발송 내역 - **경로:** `/mng/settings/sms/history` - **기능:** - 발송 내역 목록 - 검색 (수신자, 내용) - 필터 (상태, 날짜, 타입) - 재발송 - 실패 로그 확인 - **권한:** `settings.sms.history` #### 2.4 대량 문자 발송 - **경로:** `/mng/settings/sms/bulk` - **기능:** - 엑셀 업로드 (수신자 목록) - 템플릿 선택 - 예약 발송 - 발송 결과 확인 - **권한:** `settings.sms.bulk` ### DB 스키마 ```sql CREATE TABLE sms_settings ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL, provider VARCHAR(50) NOT NULL COMMENT 'aligo, kakao 등', api_key VARCHAR(255) NOT NULL COMMENT '암호화 저장', api_secret VARCHAR(255) NULL, sender_number VARCHAR(20) NOT NULL COMMENT '발신 번호', is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_tenant_id (tenant_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE sms_templates ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL, name VARCHAR(255) NOT NULL, type ENUM('sms', 'lms', 'mms') DEFAULT 'sms', content TEXT NOT NULL, variables JSON NULL, byte_count INT NOT NULL COMMENT '바이트 수', is_default BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at TIMESTAMP NULL, INDEX idx_tenant_id (tenant_id), INDEX idx_type (type) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE sms_logs ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL, template_id BIGINT UNSIGNED NULL, recipient_number VARCHAR(20) NOT NULL, content TEXT NOT NULL, type ENUM('sms', 'lms', 'mms') DEFAULT 'sms', status ENUM('pending', 'sent', 'failed') DEFAULT 'pending', sent_at TIMESTAMP NULL, failed_reason TEXT NULL, api_message_id VARCHAR(255) NULL COMMENT 'API 메시지 ID', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_tenant_id (tenant_id), INDEX idx_status (status), INDEX idx_sent_at (sent_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` ### API 엔드포인트 | Method | Endpoint | Description | FormRequest | |--------|----------|-------------|-------------| | GET | `/mng/settings/sms/api` | SMS API 설정 조회 | - | | PUT | `/mng/settings/sms/api` | SMS API 설정 저장 | `UpdateSmsApiRequest` | | POST | `/mng/settings/sms/api/test` | 테스트 문자 발송 | `TestSmsRequest` | | GET | `/mng/settings/sms/templates` | 템플릿 목록 | - | | POST | `/mng/settings/sms/templates` | 템플릿 생성 | `StoreSmsTemplateRequest` | | POST | `/mng/settings/sms/send` | 문자 발송 | `SendSmsRequest` | | POST | `/mng/settings/sms/bulk` | 대량 발송 | `SendBulkSmsRequest` | | GET | `/mng/settings/sms/history` | 발송 내역 | - | ### Service 클래스 ```php // app/Services/SmsService.php class SmsService { public function getSettings(int $tenantId = null): ?SmsSetting; public function updateSettings(array $data, int $tenantId = null): SmsSetting; public function testConnection(SmsSetting $setting, string $testNumber): bool; public function listTemplates(int $tenantId = null): Collection; public function createTemplate(array $data): SmsTemplate; public function updateTemplate(SmsTemplate $template, array $data): SmsTemplate; public function calculateByteCount(string $content): int; public function send(string $to, string $content, string $type = 'sms', int $templateId = null): SmsLog; public function sendBulk(array $recipients, string $content, string $type = 'sms'): int; public function getHistory(array $filters): LengthAwarePaginator; public function retry(SmsLog $log): bool; } ``` ### 개발 체크리스트 - [ ] `SmsSetting`, `SmsTemplate`, `SmsLog` 모델 작성 - [ ] `SmsService` 클래스 작성 - [ ] SMS API 연동 (알리고 우선) - [ ] 바이트 수 계산 로직 (한글 2바이트) - [ ] 대량 발송 큐 처리 - [ ] 엑셀 업로드 파싱 (PhpSpreadsheet) - [ ] 발송 결과 웹훅 처리 - [ ] FormRequest 작성 - [ ] i18n 키 작성 - [ ] 테스트 작성 --- ## 3️⃣ 통계 (Statistics & Analytics) ### 기능 목록 #### 3.1 대시보드 - **경로:** `/mng/dashboard` - **기능:** - 주요 KPI 카드 (회원 수, 매출, 구독 현황) - 최근 활동 로그 - 영업 파이프라인 요약 - 결제 통계 - **권한:** `dashboard.view` #### 3.2 회원 통계 - **경로:** `/mng/statistics/users` - **기능:** - 가입자 추세 (일별, 월별) - 활성/비활성 비율 - 부서별 분포 - 엑셀 내보내기 - **권한:** `statistics.users` #### 3.3 매출 통계 - **경로:** `/mng/statistics/revenue` - **기능:** - 매출 추세 (일별, 월별, 연별) - 플랜별 매출 - MRR (Monthly Recurring Revenue) - ARR (Annual Recurring Revenue) - 차트 (Chart.js 또는 ApexCharts) - **권한:** `statistics.revenue` #### 3.4 영업 통계 - **경로:** `/mng/statistics/sales` - **기능:** - 파이프라인 단계별 통계 - 전환율 (Conversion Rate) - 담당자별 성과 - 기간별 비교 - **권한:** `statistics.sales` #### 3.5 구독 통계 - **경로:** `/mng/statistics/subscriptions` - **기능:** - 플랜별 구독 현황 - 이탈률 (Churn Rate) - 업그레이드/다운그레이드 추세 - 리텐션 분석 - **권한:** `statistics.subscriptions` ### DB 스키마 ```sql -- 통계 스냅샷 (일별 집계) CREATE TABLE statistics_snapshots ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NULL COMMENT 'NULL = 전체', snapshot_date DATE NOT NULL, metric_type ENUM('users', 'revenue', 'sales', 'subscriptions') NOT NULL, metric_data JSON NOT NULL COMMENT '통계 데이터', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_tenant_id (tenant_id), INDEX idx_snapshot_date (snapshot_date), INDEX idx_metric_type (metric_type), UNIQUE KEY unique_snapshot (tenant_id, snapshot_date, metric_type) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` ### API 엔드포인트 | Method | Endpoint | Description | Query Params | |--------|----------|-------------|--------------| | GET | `/mng/dashboard` | 대시보드 데이터 | - | | GET | `/mng/statistics/users` | 회원 통계 | `start_date`, `end_date`, `group_by` | | GET | `/mng/statistics/revenue` | 매출 통계 | `start_date`, `end_date`, `group_by` | | GET | `/mng/statistics/sales` | 영업 통계 | `start_date`, `end_date` | | GET | `/mng/statistics/subscriptions` | 구독 통계 | `start_date`, `end_date` | | GET | `/mng/statistics/export` | 엑셀 내보내기 | `type`, `start_date`, `end_date` | ### Service 클래스 ```php // app/Services/StatisticsService.php class StatisticsService { public function getDashboardData(int $tenantId = null): array; public function getUserStats(array $filters): array; public function getRevenueStats(array $filters): array; public function getSalesStats(array $filters): array; public function getSubscriptionStats(array $filters): array; public function calculateMRR(int $tenantId = null): float; public function calculateARR(int $tenantId = null): float; public function calculateChurnRate(array $filters): float; public function calculateConversionRate(array $filters): float; public function exportToExcel(string $type, array $filters): string; // 파일 경로 public function createSnapshot(string $metricType, int $tenantId = null): void; // 일별 스냅샷 생성 } ``` ### UI 컴포넌트 ``` ┌─────────────────────────────────────────────────────────┐ │ 대시보드 │ ├─────────────────────────────────────────────────────────┤ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │회원 1,234│ │매출 5.2M │ │구독 567 │ │영업 89 │ │ │ │↑ 12% │ │↑ 8% │ │↓ 3% │ │↑ 15% │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐│ │ │ 매출 추세 (최근 6개월) [Chart.js 꺾은선] ││ │ └─────────────────────────────────────────────────────┘│ │ │ │ ┌─────────────────────────────────────────────────────┐│ │ │ 영업 파이프라인 [Chart.js 퍼널 차트] ││ │ └─────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────┘ ``` ### 개발 체크리스트 - [ ] `StatisticsSnapshot` 모델 작성 - [ ] `StatisticsService` 클래스 작성 - [ ] 통계 계산 로직 (MRR, ARR, Churn Rate 등) - [ ] Chart.js 또는 ApexCharts 통합 - [ ] 일별 스냅샷 스케줄러 - [ ] 엑셀 내보내기 (PhpSpreadsheet) - [ ] 대시보드 UI 작성 - [ ] 날짜 필터 UI (Alpine.js) - [ ] i18n 키 작성 - [ ] 테스트 작성 --- ## 🎯 Phase 6 완료 조건 ### 기능 완성도 - [ ] 이메일 발송 동작 - [ ] 문자 발송 동작 - [ ] 통계 차트 표시 - [ ] 엑셀 내보내기 동작 ### 코드 품질 - [ ] Service-First, FormRequest 준수 - [ ] 큐 처리 안정성 - [ ] i18n 키 사용 - [ ] Pint, PHPStan 통과 ### 외부 연동 - [ ] SMTP 연결 안정성 - [ ] SMS API 연동 동작 - [ ] 웹훅 처리 (발송 결과) ### 성능 - [ ] 대량 발송 큐 처리 - [ ] 통계 쿼리 최적화 - [ ] 스냅샷 생성 스케줄러 ### 테스트 - [ ] 이메일 발송 테스트 (Mock) - [ ] 문자 발송 테스트 (Mock) - [ ] 통계 계산 로직 테스트 - [ ] 엑셀 생성 테스트 --- ## 🎉 MNG 애플리케이션 전체 완료 Phase 6가 완료되면 MNG 애플리케이션의 핵심 기능이 모두 구현됩니다. **다음 단계:** 1. 전체 통합 테스트 2. 성능 최적화 3. 사용자 매뉴얼 작성 4. 배포 준비 **참고 문서:** - `00_OVERVIEW.md` - 전체 개발 계획 - `99_TECHNICAL_STANDARDS.md` - 기술 표준 --- **최종 업데이트:** 2025-11-21 **작성자:** Claude Code **버전:** 1.0.0