# 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 스킬로 생성되었습니다.*