# DB 백업 시스템 계획 > **작성일**: 2026-01-30 > **목적**: OS 레벨 백업(쉘 스크립트) + Laravel 모니터링 절충안으로 DB 백업 시스템 구축 > **기준 문서**: `docs/architecture/system-overview.md`, `docs/specs/database-schema.md` > **상태**: 🔄 진행중 --- ## 📍 현재 진행 상태 | 항목 | 내용 | |------|------| | **마지막 완료 작업** | Phase 5.4: 시스템 알림 Blade 페이지 + 라우트 등록 | | **다음 작업** | Phase 1.3: 개발서버 스크립트 테스트 / Phase 3: 서버 배포 | | **진행률** | 11/14 (79%) — 서버 작업 3건 잔여 | | **마지막 업데이트** | 2026-01-31 | --- ## 1. 개요 ### 1.1 배경 SAM 프로젝트의 개발서버(114.203.209.83)를 당분간 운영 환경처럼 사용할 예정이므로, 데이터 손실 방지를 위한 DB 백업 시스템이 필요하다. 운영서버에도 동일 구조로 적용할 수 있도록 설계한다. **대상 데이터베이스:** - `sam` — 메인 비즈니스 데이터 (개발서버), `samdb` (로컬 Docker) - `sam_stat` — 통계 데이터 (재집계 가능하나 함께 백업) ### 1.2 기준 원칙 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🎯 핵심 원칙 │ ├─────────────────────────────────────────────────────────────────┤ │ 1. 백업은 OS 레벨(crontab)에서 실행 — 앱 장애와 무관하게 동작 │ │ 2. 모니터링은 Laravel에서 — 기존 stat_alerts 인프라 활용 │ │ 3. 환경 이식성 — backup.conf만 수정하면 운영서버에서도 동작 │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.3 변경 승인 정책 | 분류 | 예시 | 승인 | |------|------|------| | ✅ 즉시 가능 | 스크립트 파일 생성, .conf 파일 생성, 문서 수정 | 불필요 | | ⚠️ 컨펌 필요 | StatMonitorService 수정, 스케줄러 등록, crontab 등록 | **필수** | | 🔴 금지 | 기존 테이블 구조 변경, 기존 스케줄 시간 변경 | 별도 협의 | ### 1.4 준수 규칙 - `docs/quickstart/quick-start.md` - 빠른 시작 가이드 - `docs/standards/quality-checklist.md` - 품질 체크리스트 - `docs/standards/api-rules.md` - API 개발 규칙 (Service-First) ### 1.5 환경 정보 #### 개발서버 (배포 대상) ``` SSH: hskwon@114.203.209.83 API 경로: /home/webservice/api MNG 경로: /home/webservice/mng MySQL: 8.0.44 DB 사용자: codebridge / code**bridge DB명: sam (메인), sam_stat (통계) ※ 로컬 Docker에서는 samdb (메인) Git remote: /data/GIT/samproject/sam-api (bare repo, post-receive hook으로 auto-deploy) MNG remote: /data/GIT/samproject/sam-mng ``` #### 로컬 (코드 작업) ``` 프로젝트 루트: /Users/kent/Works/@KD_SAM/SAM/ API: api/ (Laravel 12, PHP 8.4) MNG: mng/ (Laravel 12, Plain Blade + HTMX + Tailwind) Docker: docker/ (docker-compose.yml) 로컬 DB: samdb (메인), sam_stat (통계), samuser/sampass ``` #### 배포 프로세스 ``` 로컬에서 코드 작성 → git add + git commit → git push origin develop (api) → 개발서버 post-receive hook이 자동 pull + migrate → MNG도 동일 (git push → auto-deploy) ``` ### 1.6 기존 코드 참조 (필수 읽기) 새 세션에서 작업 시작 전 반드시 읽어야 할 기존 코드: | 파일 | 이유 | Phase | |------|------|-------| | `api/app/Services/Stats/StatMonitorService.php` | recordBackupFailure() 추가 대상 | 2, 4 | | `api/app/Models/Stats/BaseStatModel.php` | sam_stat 연결 패턴 ($connection = 'sam_stat') | 5 | | `api/app/Models/Stats/StatAlert.php` | 알림 모델 구조 (MNG용 모델 생성 참조) | 5 | | `api/routes/console.php` | 기존 스케줄러 패턴 (Schedule::command 형식) | 2 | | `mng/app/Http/Controllers/AuditLogController.php` | MNG 컨트롤러 패턴 (필터+페이지네이션) | 5 | | `mng/routes/web.php` | MNG 라우트 등록 패턴 | 5 | #### stat_alerts 테이블 스키마 ```sql -- sam_stat 데이터베이스 CREATE TABLE stat_alerts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id INT UNSIGNED NOT NULL, alert_type VARCHAR(50) NOT NULL, -- aggregation_failure, missing_data, data_mismatch, backup_failure domain VARCHAR(50) NOT NULL, -- sales, finance, production, backup, system 등 severity ENUM('info','warning','critical') NOT NULL, title VARCHAR(200) NOT NULL, message TEXT, current_value DECIMAL(15,2) NULL, threshold_value DECIMAL(15,2) NULL, is_read TINYINT(1) DEFAULT 0, is_resolved TINYINT(1) DEFAULT 0, resolved_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### BaseStatModel 패턴 (API) ```php // api/app/Models/Stats/BaseStatModel.php abstract class BaseStatModel extends Model { protected $connection = 'sam_stat'; protected $guarded = ['id']; } // api/app/Models/Stats/StatAlert.php class StatAlert extends BaseStatModel { protected $table = 'stat_alerts'; public $timestamps = false; protected $casts = [ 'current_value' => 'decimal:2', 'threshold_value' => 'decimal:2', 'is_read' => 'boolean', 'is_resolved' => 'boolean', 'resolved_at' => 'datetime', 'created_at' => 'datetime', ]; } ``` #### StatMonitorService 현재 메서드 ```php // api/app/Services/Stats/StatMonitorService.php class StatMonitorService { public function recordAggregationFailure(int $tenantId, string $domain, string $jobType, string $errorMessage): void public function recordMissingData(int $tenantId, string $domain, string $date): void public function recordMismatch(int $tenantId, string $domain, string $label, float|int $expected, float|int $actual): void public function resolveAlerts(int $tenantId, string $domain, string $alertType): int } // 모든 메서드는 try/catch로 감싸져 있음 (실패해도 비즈니스 로직 차단 안 함) ``` #### routes/console.php 스케줄 등록 패턴 ```php // 기존 패턴 — 이 형식을 따라야 함 Schedule::command('db:backup-check') ->dailyAt('05:00') ->appendOutputTo(storage_path('logs/scheduler.log')) ->onSuccess(function () { \Illuminate\Support\Facades\Log::info('✅ db:backup-check 스케줄러 실행 성공', ['time' => now()]); }) ->onFailure(function () { \Illuminate\Support\Facades\Log::error('❌ db:backup-check 스케줄러 실행 실패', ['time' => now()]); }); ``` #### MNG 컨트롤러 패턴 ```php // mng/app/Http/Controllers/AuditLogController.php (참조 패턴) class AuditLogController extends Controller { public function index(Request $request): View { $query = Model::query()->orderByDesc('created_at'); // 필터 적용 (if $request->filled('xxx')) // 페이지네이션: $query->paginate(50)->withQueryString() return view('...', compact(...)); } } ``` #### MNG 라우트 등록 패턴 ```php // mng/routes/web.php (기존 패턴) Route::prefix('audit-logs')->name('audit-logs.')->group(function () { Route::get('/', [AuditLogController::class, 'index'])->name('index'); Route::get('/{id}', [AuditLogController::class, 'show'])->name('show'); }); // 새로 추가할 패턴 Route::prefix('system/alerts')->name('system.alerts.')->group(function () { Route::get('/', [SystemAlertController::class, 'index'])->name('index'); Route::post('/{id}/read', [SystemAlertController::class, 'markAsRead'])->name('read'); Route::post('/{id}/resolve', [SystemAlertController::class, 'resolve'])->name('resolve'); Route::post('/read-all', [SystemAlertController::class, 'markAllAsRead'])->name('read-all'); }); ``` #### MNG 주의사항 (CLAUDE.md 기반) ``` - MNG에서 마이그레이션 파일 생성 금지 (API에서만 관리) - MNG에서 모델 작성은 허용 (API의 테이블 사용) - HTMX 사용: 읽음/해결 버튼은 hx-post로 처리 - 사이드바 메뉴 추가 시: MngMenuSeeder 수정 + db:seed 실행 필요 ``` --- ## 2. 대상 범위 ### 2.1 Phase 1: 백업 스크립트 (A안 — OS 레벨) | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1.1 | backup.conf 설정 파일 생성 | ✅ | DB 접속정보, 경로, 보관기간 | | 1.2 | sam-db-backup.sh 스크립트 생성 | ✅ | mysqldump + gzip + 보관관리 | | 1.3 | 개발서버에서 스크립트 테스트 | ⏳ | 수동 실행 후 백업 파일 확인 (서버 접속 필요) | ### 2.2 Phase 2: Laravel 모니터링 (B안 — 앱 레벨) | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 2.1 | StatMonitorService에 recordBackupFailure() 추가 | ✅ | 기존 서비스 확장 | | 2.2 | BackupCheckCommand 생성 | ✅ | db:backup-check 커맨드 | | 2.3 | routes/console.php에 스케줄 등록 | ✅ | 매일 05:00 실행 | ### 2.3 Phase 3: 서버 배포 & 테스트 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 3.1 | 개발서버 crontab 등록 | ⏳ | 백업 스크립트 + schedule:run 확인 (서버 접속 필요) | | 3.2 | 통합 테스트 (백업→모니터링) | ⏳ | 전체 플로우 검증 (서버 접속 필요) | ### 2.4 Phase 4: Slack 알림 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 4.1 | SlackNotificationService 생성 | ✅ | 웹훅 기반 알림 발송 서비스 | | 4.2 | BackupCheckCommand에 Slack 알림 연동 | ✅ | 백업 실패 시 Slack 즉시 통보 | | 4.3 | StatMonitorService에 Slack 알림 연동 | ✅ | 집계 실패/정합성 불일치 시 통보 (critical만) | | 4.4 | 개발서버 테스트 | ⏳ | 실제 Slack 채널에 테스트 메시지 전송 (Phase 3과 함께) | ### 2.5 Phase 5: MNG 관리자 패널 — 시스템 알림 페이지 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 5.1 | MNG에 sam_stat DB 연결 추가 | ✅ | config/database.php + .env | | 5.2 | StatAlert 모델 생성 (MNG용) | ✅ | sam_stat 연결, 읽기 전용 | | 5.3 | SystemAlertController 생성 | ✅ | 목록 조회, 읽음 처리, 해결 처리 | | 5.4 | 시스템 알림 Blade 페이지 생성 | ✅ | 필터링, 페이지네이션, 상태관리 + 라우트 등록 | --- ## 3. 작업 절차 ### 3.1 아키텍처 개요 ``` [OS 레벨 — crontab] [앱 레벨 — Laravel API] 04:30 sam-db-backup.sh 05:00 db:backup-check ├── mysqldump sam → gzip ├── 오늘 백업 파일 존재? ├── mysqldump sam_stat → gzip ├── 파일 크기 최소값 충족? ├── 오래된 백업 삭제 (보관정책) ├── 마지막 백업 25시간 이내? └── 상태 파일 기록 ├── 실패 시 stat_alerts 기록 (.backup_status) │ (domain=backup, severity=critical) └── 실패 시 Slack 웹훅 전송 ↓ SlackNotificationService ├── 백업 실패 알림 ├── 집계 실패 알림 └── 정합성 불일치 알림 [MNG 관리자 패널] mng.sam.kr/system/alerts ├── stat_alerts 목록 조회 (sam_stat DB) ├── 필터: 도메인, 심각도, 읽음/미읽음 ├── 읽음 처리 └── 해결 처리 ``` ### 3.2 스케줄 시간표 (최종) ``` 02:00 stat:aggregate-daily (Laravel) 03:00 stat:aggregate-monthly (Laravel, 월 1일만) 03:00 api-log:prune (Laravel) 03:10 audit:prune (Laravel) 03:20 sanctum:prune-expired (Laravel) 03:30 storage:cleanup-temp (Laravel) 03:40 storage:cleanup-trash (Laravel) 03:50 storage:cleanup-links (Laravel) 04:00 storage:record-usage (Laravel) 04:30 sam-db-backup.sh (crontab — OS 레벨) 05:00 db:backup-check (Laravel) 09:00 stat:check-kpi-alerts (Laravel) ``` ### 3.3 디렉토리 구조 ``` /data/backup/mysql/ ├── daily/ │ ├── 2026-01-30/ │ │ ├── sam_20260130_0430.sql.gz │ │ └── sam_stat_20260130_0430.sql.gz │ ├── 2026-01-29/ │ └── ... (7일 보관) ├── weekly/ │ ├── sam_20260126_week.sql.gz │ └── ... (4주 보관) └── logs/ └── backup.log ``` ### 3.4 프로젝트 내 파일 구조 ``` api/ ├── scripts/ │ └── backup/ │ ├── sam-db-backup.sh # 백업 스크립트 │ └── backup.conf.example # 설정 파일 예시 (Git 추적) ├── app/ │ ├── Console/Commands/ │ │ └── BackupCheckCommand.php # 모니터링 커맨드 │ └── Services/ │ ├── Stats/ │ │ └── StatMonitorService.php # recordBackupFailure() 추가 │ └── SlackNotificationService.php # Slack 웹훅 알림 서비스 └── routes/ └── console.php # 스케줄 등록 추가 mng/ ├── app/ │ ├── Http/Controllers/ │ │ └── System/ │ │ └── SystemAlertController.php # 시스템 알림 컨트롤러 │ └── Models/ │ └── Stats/ │ └── StatAlert.php # 알림 모델 (sam_stat 연결) ├── config/ │ └── database.php # sam_stat 연결 추가 ├── resources/views/ │ └── system/ │ └── alerts/ │ └── index.blade.php # 시스템 알림 목록 페이지 └── routes/ └── web.php # /system/alerts 라우트 추가 ``` --- ## 4. 상세 작업 내용 ### 4.1 Phase 1: 백업 스크립트 #### 1.1 backup.conf.example 설정 파일 (서버에 `backup.conf`로 복사 후 수정): ```bash # DB 접속 정보 DB_HOST=127.0.0.1 DB_PORT=3306 DB_USER=codebridge DB_PASS="code**bridge" # 백업 대상 DB (공백 구분) DATABASES="sam sam_stat" # 백업 저장 경로 BACKUP_BASE_DIR=/data/backup/mysql # 보관 정책 DAILY_RETENTION_DAYS=7 WEEKLY_RETENTION_DAYS=28 # 로그 LOG_FILE=/data/backup/mysql/logs/backup.log # 상태 파일 (Laravel 모니터링용) STATUS_FILE=/data/backup/mysql/.backup_status ``` #### 1.2 sam-db-backup.sh 주요 로직 ``` 1. backup.conf 로드 2. 날짜 디렉토리 생성 (daily/YYYY-MM-DD/) 3. 각 DB별 mysqldump 실행 - --single-transaction (InnoDB 무중단) - --routines --triggers (프로시저/트리거 포함) - | gzip 압축 4. 일요일이면 weekly/ 디렉토리에도 복사 5. 오래된 백업 삭제 - daily: DAILY_RETENTION_DAYS일 초과 삭제 - weekly: WEEKLY_RETENTION_DAYS일 초과 삭제 6. 상태 파일 기록 (성공/실패, 파일 크기, 시간) 7. 로그 기록 ``` #### 1.3 상태 파일 형식 (.backup_status) ```json { "last_run": "2026-01-30T04:30:00+09:00", "status": "success", "databases": { "sam": {"file": "sam_20260130_0430.sql.gz", "size_bytes": 52428800}, "sam_stat": {"file": "sam_stat_20260130_0430.sql.gz", "size_bytes": 1048576} }, "errors": [] } ``` ### 4.2 Phase 2: Laravel 모니터링 #### 2.1 StatMonitorService 확장 ```php // 추가 메서드 public function recordBackupFailure(int $tenantId, string $title, string $message): void // domain: 'backup', alert_type: 'backup_failure', severity: 'critical' ``` **참고**: 백업은 테넌트 무관(시스템 레벨)이므로 tenantId=0 사용 #### 2.2 BackupCheckCommand ``` 시그니처: db:backup-check 옵션: --path= (백업 경로 오버라이드) 체크 항목: 1. .backup_status 파일 존재 여부 2. last_run이 25시간 이내인지 3. status가 "success"인지 4. 각 DB 백업 파일 크기가 최소값 이상인지 - sam: 1MB 이상 - sam_stat: 100KB 이상 결과: - 모든 체크 통과: "✅ 백업 상태 정상" 출력 - 하나라도 실패: stat_alerts에 기록 + "❌ 백업 이상 감지" 출력 ``` #### 2.3 환경 설정 ```env # .env 추가 BACKUP_PATH=/data/backup/mysql BACKUP_STATUS_FILE=/data/backup/mysql/.backup_status BACKUP_MIN_SIZE_SAM=1048576 BACKUP_MIN_SIZE_STAT=102400 ``` ### 4.3 Phase 4: Slack 알림 #### 4.1 SlackNotificationService ``` 위치: api/app/Services/SlackNotificationService.php 기능: - Slack Incoming Webhook을 통한 메시지 전송 - 기존 LOG_SLACK_WEBHOOK_URL 환경변수 활용 - 별도 SLACK_ALERT_WEBHOOK_URL 추가 (알림 전용 채널 분리 가능) 메서드: - sendAlert(string $title, string $message, string $severity): void └── severity에 따른 색상: critical=red, warning=orange, info=blue - sendBackupAlert(string $title, string $message): void - sendStatAlert(string $title, string $message, string $domain): void 메시지 포맷 (Slack Block Kit): ┌──────────────────────────────────────┐ │ 🚨 [SAM 백업 실패] │ │ │ │ 서버: 개발서버 (114.203.209.83) │ │ 시간: 2026-01-30 05:00:00 │ │ 상세: sam DB 백업 파일 미발견 │ │ │ │ 환경: development │ └──────────────────────────────────────┘ ``` #### 4.2 BackupCheckCommand Slack 연동 ``` 기존 흐름: 체크 실패 → stat_alerts 기록 → 로그 출력 변경 후: 체크 실패 → stat_alerts 기록 → Slack 알림 전송 → 로그 출력 ``` #### 4.3 StatMonitorService Slack 연동 ``` 기존 흐름: 집계 실패/정합성 불일치 → stat_alerts 기록 변경 후: 집계 실패/정합성 불일치 → stat_alerts 기록 → Slack 알림 전송 (severity가 critical인 경우에만 Slack 전송) ``` #### 4.4 환경 설정 ```env # .env 추가 SLACK_ALERT_WEBHOOK_URL= # 알림 전용 채널 (미설정 시 LOG_SLACK_WEBHOOK_URL 사용) SLACK_ALERT_ENABLED=true # Slack 알림 활성화 여부 SLACK_ALERT_SERVER_NAME=개발서버 # 메시지에 표시할 서버명 ``` ### 4.4 Phase 5: MNG 관리자 패널 #### 5.1 MNG에 sam_stat DB 연결 추가 ```php // mng/config/database.php - connections 배열에 추가 'sam_stat' => [ 'driver' => 'mysql', 'host' => env('STAT_DB_HOST', env('DB_HOST', '127.0.0.1')), 'port' => env('STAT_DB_PORT', env('DB_PORT', '3306')), 'database' => env('STAT_DB_DATABASE', 'sam_stat'), 'username' => env('STAT_DB_USERNAME', env('DB_USERNAME')), 'password' => env('STAT_DB_PASSWORD', env('DB_PASSWORD')), // ... 기본 설정 ], ``` ```env # mng/.env 추가 STAT_DB_HOST=127.0.0.1 STAT_DB_PORT=3306 STAT_DB_DATABASE=sam_stat STAT_DB_USERNAME=samuser STAT_DB_PASSWORD=sampass ``` #### 5.2 StatAlert 모델 (MNG용) ```php // mng/app/Models/Stats/StatAlert.php // - connection: sam_stat // - 읽기 전용 (조회 + 상태 변경만) // - fillable: is_read, is_resolved, resolved_at ``` #### 5.3 SystemAlertController ``` 라우트: /system/alerts 미들웨어: auth, hq.member, password.changed 기능: GET /system/alerts — 알림 목록 (필터/페이지네이션) POST /system/alerts/{id}/read — 읽음 처리 POST /system/alerts/{id}/resolve — 해결 처리 POST /system/alerts/read-all — 전체 읽음 처리 필터 파라미터: - domain: backup, sales, finance, production, system 등 - severity: info, warning, critical - status: all, unread, unresolved - date_from, date_to ``` #### 5.4 알림 목록 Blade 페이지 ``` 페이지: mng/resources/views/system/alerts/index.blade.php 레이아웃: 기존 MNG 레이아웃 (사이드바 + 헤더) UI 구성: ┌─────────────────────────────────────────────────────────┐ │ 시스템 알림 [전체 읽음]│ ├─────────────────────────────────────────────────────────┤ │ 필터: [도메인 ▼] [심각도 ▼] [상태 ▼] [날짜 범위] │ ├─────────────────────────────────────────────────────────┤ │ 🔴 [backup] 백업 실패 — sam DB 백업 파일 미발견 │ │ 2026-01-30 05:00 │ 미읽음 │ 미해결 │ [읽음] [해결] │ ├─────────────────────────────────────────────────────────┤ │ 🟡 [sales] 2026-01-29 데이터 누락 │ │ 2026-01-30 02:05 │ 읽음 │ 미해결 │ [해결] │ ├─────────────────────────────────────────────────────────┤ │ 🔴 [finance] deposit_amount 정합성 불일치 │ │ 2026-01-29 02:10 │ 읽음 │ 해결됨 │ │ ├─────────────────────────────────────────────────────────┤ │ < 1 2 3 ... > │ └─────────────────────────────────────────────────────────┘ 심각도 색상: critical=빨강, warning=노랑, info=파랑 HTMX 활용: 읽음/해결 버튼 클릭 시 페이지 리로드 없이 상태 변경 ``` --- ## 5. 컨펌 대기 목록 | # | 항목 | 변경 내용 | 영향 범위 | 상태 | |---|------|----------|----------|------| | 1 | StatMonitorService 수정 | recordBackupFailure() 메서드 추가 + Slack 연동 | api/Services | ✅ 완료 | | 2 | routes/console.php 수정 | db:backup-check 스케줄 등록 (05:00) | api/스케줄러 | ✅ 완료 | | 3 | crontab 등록 | 개발서버에 sam-db-backup.sh 등록 (04:30) | 서버 | ⏳ 서버 배포 시 | | 4 | SlackNotificationService 생성 | Slack 웹훅 알림 서비스 신규 | api/Services | ✅ 완료 | | 5 | StatMonitorService Slack 연동 | critical 알림 시 Slack 전송 | api/Services | ✅ 완료 | | 6 | MNG database.php 수정 | sam_stat 연결 추가 | mng/config | ✅ 완료 | | 7 | MNG web.php 수정 | /system/alerts 라우트 추가 | mng/routes | ✅ 완료 | --- ## 6. 변경 이력 | 날짜 | 항목 | 변경 내용 | 파일 | 승인 | |------|------|----------|------|------| | 2026-01-30 | - | 문서 초안 작성 | - | - | | 2026-01-31 | Phase 1.1 | backup.conf.example 생성 | api/scripts/backup/backup.conf.example | ✅ | | 2026-01-31 | Phase 1.2 | sam-db-backup.sh 스크립트 생성 | api/scripts/backup/sam-db-backup.sh | ✅ | | 2026-01-31 | Phase 2.1 | recordBackupFailure() 추가 | api/app/Services/Stats/StatMonitorService.php | ✅ | | 2026-01-31 | Phase 2.2 | BackupCheckCommand 생성 | api/app/Console/Commands/BackupCheckCommand.php | ✅ | | 2026-01-31 | Phase 2.3 | db:backup-check 스케줄 등록 | api/routes/console.php | ✅ | | 2026-01-31 | Phase 4.1 | SlackNotificationService 생성 | api/app/Services/SlackNotificationService.php | ✅ | | 2026-01-31 | Phase 4.2 | BackupCheckCommand Slack 연동 | api/app/Console/Commands/BackupCheckCommand.php | ✅ | | 2026-01-31 | Phase 4.3 | StatMonitorService Slack 연동 | api/app/Services/Stats/StatMonitorService.php | ✅ | | 2026-01-31 | Phase 4.3 | .env.example 환경변수 추가 | api/.env.example | ✅ | | 2026-01-31 | Phase 5.1 | sam_stat DB 연결 추가 | mng/config/database.php | ✅ | | 2026-01-31 | Phase 5.2 | StatAlert 모델 생성 (MNG) | mng/app/Models/Stats/StatAlert.php | ✅ | | 2026-01-31 | Phase 5.3 | SystemAlertController 생성 | mng/app/Http/Controllers/System/SystemAlertController.php | ✅ | | 2026-01-31 | Phase 5.4 | 시스템 알림 Blade + 라우트 | mng/resources/views/system/alerts/index.blade.php, mng/routes/web.php | ✅ | --- ## 7. 참고 문서 - **빠른 시작**: `docs/quickstart/quick-start.md` - **품질 체크리스트**: `docs/standards/quality-checklist.md` - **시스템 아키텍처**: `docs/architecture/system-overview.md` - **DB 스키마**: `docs/specs/database-schema.md` - **기존 스케줄러**: `api/routes/console.php` - **StatMonitorService**: `api/app/Services/Stats/StatMonitorService.php` - **StatAlert 모델**: `api/app/Models/Stats/StatAlert.php` - **sam_stat 설계**: `docs/dev_plans/sam-stat-database-design-plan.md` - **MNG 라우트**: `mng/routes/web.php` - **MNG 레이아웃**: `mng/resources/views/layouts/` - **Slack 웹훅**: `api/.env` → `LOG_SLACK_WEBHOOK_URL` --- ## 8. 세션 및 메모리 관리 정책 (Serena Optimized) ### 8.1 세션 시작 시 (Load Strategy) ```javascript read_memory("db-backup-state") read_memory("db-backup-snapshot") read_memory("db-backup-active-symbols") ``` ### 8.2 작업 중 관리 (Context Defense) | 컨텍스트 잔량 | Action | 내용 | |--------------|--------|------| | **30% 이하** | Snapshot | `write_memory("db-backup-snapshot", "코드변경+논의요약")` | | **20% 이하** | Context Purge | `write_memory("db-backup-active-symbols", "주요 수정 파일/함수")` | | **10% 이하** | Stop & Save | 최종 상태 저장 후 세션 교체 권고 | ### 8.3 Serena 메모리 구조 - `db-backup-state`: { phase, progress, next_step, last_decision } - `db-backup-snapshot`: 현재까지의 논의 및 코드 변경점 요약 - `db-backup-active-symbols`: 현재 수정 중인 파일/심볼 리스트 --- ## 9. 검증 결과 > 작업 완료 후 이 섹션에 검증 결과 추가 ### 9.1 테스트 케이스 | 입력값 | 예상 결과 | 실제 결과 | 상태 | |--------|----------|----------|------| | sam-db-backup.sh 수동 실행 | daily/ 디렉토리에 .sql.gz 2개 생성 | | ⏳ | | .backup_status 확인 | JSON 형식, status=success | | ⏳ | | db:backup-check 실행 (백업 정상) | "백업 상태 정상" 출력 | | ⏳ | | db:backup-check 실행 (백업 없음) | stat_alerts 기록 + Slack 알림 전송 | | ⏳ | | 8일 후 daily/ 확인 | 7일 초과 백업 자동 삭제 | | ⏳ | | Slack 테스트 메시지 전송 | 지정 채널에 메시지 수신 확인 | | ⏳ | | MNG /system/alerts 접속 | 알림 목록 표시, 필터 동작 | | ⏳ | | MNG 읽음/해결 처리 | 상태 변경 후 DB 반영 확인 | | ⏳ | ### 9.2 성공 기준 달성 현황 | 기준 | 달성 | 비고 | |------|------|------| | sam + sam_stat 백업 파일 생성 | ⏳ | | | gzip 압축 적용 | ⏳ | | | 보관 정책 (일간 7일, 주간 4주) 동작 | ⏳ | | | Laravel 모니터링으로 백업 상태 확인 | ⏳ | | | 실패 시 stat_alerts 기록 | ⏳ | | | 실패 시 Slack 알림 전송 | ⏳ | | | MNG에서 알림 목록 조회 가능 | ⏳ | | | MNG에서 읽음/해결 처리 가능 | ⏳ | | | 운영서버 이식성 (backup.conf + .env 수정만으로 동작) | ⏳ | | --- ## 10. 자기완결성 점검 결과 ### 10.1 체크리스트 검증 | # | 검증 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1 | 작업 목적이 명확한가? | ✅ | 1.1 배경에 명시 | | 2 | 성공 기준이 정의되어 있는가? | ✅ | 9.2 성공 기준 9개 | | 3 | 작업 범위가 구체적인가? | ✅ | 5 Phase, 14 항목 | | 4 | 의존성이 명시되어 있는가? | ✅ | 기존 stat_alerts 인프라 활용 | | 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 7에 명시 | | 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 4 상세 내용 | | 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 9 테스트 케이스 | | 8 | 모호한 표현이 없는가? | ✅ | 크기/시간/경로 모두 구체적 | ### 10.2 새 세션 시뮬레이션 테스트 | 질문 | 답변 가능 | 참조 섹션 | |------|:--------:|----------| | Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | | Q2. 어디서부터 시작해야 하는가? | ✅ | 2.1 Phase 1 | | Q3. 어떤 파일을 수정해야 하는가? | ✅ | 3.4 파일 구조 | | Q4. 작업 완료 확인 방법은? | ✅ | 9. 검증 결과 | | Q5. 막혔을 때 참고 문서는? | ✅ | 7. 참고 문서 | **결과**: 5/5 통과 → ✅ 자기완결성 확보 --- *이 문서는 /sc:plan 스킬로 생성되었습니다.*