Files
sam-manage/docs/06_PHASE6_COMM_STATS.md

500 lines
18 KiB
Markdown
Raw Normal View History

ㅑ# 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