# GLOBEAU 플랫폼 운영 환경설정(settings) 기술 명세서

## 제1장. 개요 및 설계 철학 (Overview & Architecture)

### 1.1 도입 목적
- **동적 설정 변경**: 서버 재배포나 라라벨 설정 캐시(`config:cache`) 갱신 없이, 웹 관리자 화면에서 플랫폼 운영 정책을 실시간으로 반영합니다.
- **인프라 보안과 운영 비즈니스의 분리**: 
  - **`.env` (인프라 보안)**: DB 패스워드, 통신사 API Key, 웹훅 시크릿 등 노출 시 서버 전체에 치명적인 보안 정보만 격리 보관합니다.
  - **`settings` 테이블 (운영 비즈니스)**: 본사 관리자 통보 번호, 080 수신거부 번호, 야간 발송 제한, 뷰티샵 수수료율 등 운영자가 상시 점검하고 변경해야 하는 비즈니스 필드를 관리합니다.

### 1.2 핵심 동작 원칙
1. **계층적 폴백(Fallback) 구조**: 
   - 1순위: `settings` 테이블의 DB 저장값
   - 2순위: DB 값이 `null`이거나 빈 문자열(`""`)일 경우 `.env` 기본값
   - 3순위: 시스템 하드코딩 안전 기본값
2. **고성능 메모리 캐싱 (Redis/File Cache)**:
   - 매 요청마다 발생하는 DB 조회를 방지하기 위해 조회된 설정값은 라라벨 캐시(`Cache::remember`)로 관리되며, 관리자가 값을 수정하는 즉시 캐시가 자동 무효화(`Cache::forget`)됩니다.

---

## 제2장. 테이블 상세 명세서 (Schema Definition)

* **물리 테이블명**: `settings`
* **논리 테이블명**: 플랫폼 운영 환경설정 마스터

| 번호 | 구분 | 컬럼명 | 타입 | Null | 기본값 | 인덱스/제약조건 | 설명 및 상태값 |
| :---: | :---: | :--- | :--- | :---: | :---: | :--- | :--- |
| **1** | 기본키 | `id` | Bigint (unsigned) | N | Auto Increment | PK | 설정 레코드 고유 식별 번호 |
| **2** | 그룹분류 | `group` | Varchar(50) | N | 'general' | Index | 설정 업무 도메인 분류<br>- `notification`: 알림 및 메시징 설정<br>- `shop`: 뷰티샵 운영 및 입점 정책<br>- `site`: 플랫폼 기본 운영/점검<br>- `billing`: 포인트 및 결제/정산 |
| **3** | 고유키 | `key` | Varchar(100) | N | - | Unique Index | 설정 항목 고유 식별 키 (Snake Case 표기) |
| **4** | 설정값 | `value` | Text | Y | NULL | - | 실제 운영 설정 데이터<br>- 단일 문자열, 숫자, 불리언(1/0)<br>- 복합 데이터는 JSON 규격 직렬화 저장 |
| **5** | 설명문 | `description` | Varchar(255) | Y | NULL | - | 관리자 화면 안내용 항목 설명 및 가이드 |
| **6** | 일시 | `created_at` | Timestamp | Y | NULL | - | 레코드 최초 생성 일시 |
| **7** | 일시 | `updated_at` | Timestamp | Y | NULL | - | 레코드 최종 변경/갱신 일시 |

---

## 제3장. 도메인별 설정 카탈로그 (Configuration Catalog)

### 3.1 알림 및 메시징 모듈 (`group = 'notification'`)

| 키 이름 (`key`) | 데이터 타입 | 기본값 / 초기값 | 폴백(`.env`) 대상 | 세부 운영 기준 및 법적 준수 사항 |
|---|:---:|:---:|:---:|---|
| `notification_admin_phones` | 문자열 (콤마 구분) | `null` | `env('ADMIN_PHONE_NUMBER')` | **신규 입점 신청 및 긴급 비상 통보를 수신할 본사 관리자 번호**<br>- 콤마(`,`)로 다중 번호 등록 가능<br>- 미등록 시 `.env`의 `ADMIN_PHONE_NUMBER`로 자동 폴백 |
| `notification_080_phone` | 문자열 | `'080-800-0000'` | `env('SMS_080_PHONE')` | **광고성 메시지 표기용 080 무료 수신거부 번호**<br>- 정보통신망법 제50조 필수 규정<br>- 통신사 080 ARS 서비스 개통 번호 입력 |
| `notification_night_block` | 정수 (Boolean) | `'1'` | - | **야간 광고 발송 제한(20:00~08:00) 적용 여부**<br>- `1`: 차단(익일 08:05 예약 이월), `0`: 즉시 전송<br>- **★ 중요: 시스템 정보성 알림(인증, 입점접수, 예약확정)은 이 설정과 무관하게 24시간 즉시 발송** |

> **통신사 대표 발신번호 제외 규정**:  
> 카페24 통신사에 서류 인증된 대표 발신번호(`CAFE24_SMS_SENDER`)는 임의 변경 시 통신사 발송 차단(에러코드 반환)이 발생하므로 `settings` 테이블에서 제외하고 서버 설정(`config/services.php`)으로 안전하게 고정합니다.

### 3.2 플랫폼 및 뷰티샵 확장 모듈 (차후 확장 예시)

| 그룹 (`group`) | 키 이름 (`key`) | 데이터 타입 | 설명 |
|---|---|:---:|---|
| `shop` | `shop_initial_free_points` | 정수 | 뷰티샵 신규 입점 승인 시 자동 무상 지급할 기본 메시지 포인트 (예: `5000`) |
| `shop` | `shop_point_sms_unit` | 정수 | 뷰티샵 메시지 발송 시 차감할 건당 포인트 (SMS: `15`, LMS: `40`) |
| `site` | `site_maintenance_mode` | 불리언 | 플랫폼 긴급 점검 모드 활성화 여부 (`0` / `1`) |

---

## 제4장. 백엔드 구현 및 캐싱 아키텍처

### 4.1 Eloquent 모델 및 자동 캐싱 처리 (`app/Models/Setting.php`)

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Support\Facades\Cache;

    class Setting extends Model
    {
        protected $fillable = ['group', 'key', 'value', 'description'];

        /**
         * 캐시 TTL 설정 (1일 = 86,400초)
         */
        protected const CACHE_TTL = 86400;

        /**
         * 설정값 조회 (캐시 우선 확인 + Fallback 지원)
         */
        public static function get(string $key, mixed $default = null): mixed
        {
            return Cache::remember("setting.{$key}", self::CACHE_TTL, function () use ($key, $default) {
                $setting = self::where('key', $key)->first();
                return ($setting && !is_null($setting->value) && trim((string)$setting->value) !== '')
                    ? $setting->value
                    : $default;
            });
        }

        /**
         * 설정값 저장/수정 (저장 즉시 캐시 무효화)
         */
        public static function set(string $key, mixed $value, string $group = 'general', ?string $description = null): void
        {
            self::updateOrCreate(
                ['key' => $key],
                [
                    'group'       => $group,
                    'value'       => $value,
                    'description' => $description,
                ]
            );

            // 캐시 즉시 삭제 (다음 조회 시 DB에서 최신값 갱신)
            Cache::forget("setting.{$key}");
        }

        /**
         * 특정 도메인 그룹 전체 설정 조회
         */
        public static function getGroup(string $group): array
        {
            return self::where('group', $group)
                ->pluck('value', 'key')
                ->toArray();
        }
    }

### 4.2 관리자 번호 추출 비즈니스 로직 (`NotificationService.php`)

    /**
     * 본사 비상 알림 수신 관리자 전화번호 정제 목록 반환
     * - 1순위: settings 테이블 (notification_admin_phones)
     * - 2순위: .env (ADMIN_PHONE_NUMBER)
     * 
     * @return array<string>
     */
    public function getAdminPhoneNumbers(): array
    {
        // 1. DB 설정값 우선 확인, 없을 경우 .env 폴백
        $rawPhones = \App\Models\Setting::get(
            'notification_admin_phones', 
            env('ADMIN_PHONE_NUMBER', '')
        );

        if (empty($rawPhones)) {
            return [];
        }

        // 2. 콤마 분리 및 특수문자 제거(숫자만 정제)
        return array_values(array_filter(
            array_map(function ($phone) {
                return preg_replace('/[^0-9]/', '', trim($phone));
            }, explode(',', (string)$rawPhones))
        ));
    }

---

## 제5장. 보안 및 운영 관리 수칙 (Security & Governance)

1. **접근 권한 엄격 통제 (RBAC)**:
   - `settings` 테이블의 데이터를 조회하고 수정하는 모든 라우트(`admin.notifications.settings.*`)는 최고 관리자 권한(`permission:notifications.settings` 또는 Super Admin)을 가진 계정만 접근 가능하도록 미들웨어로 보호합니다.
2. **보안 민감 정보 등록 금지**:
   - DB 백업본 유출이나 SQL 인젝션 등에 대비하여, 본 테이블에는 **API Secret, 토큰, 비밀번호 등 암호화되지 않은 기밀 인증 정보는 절대 저장하지 않습니다.** (반드시 서버 `.env` 유지)
3. **입력 데이터 유효성 검사**:
   - 관리자 화면에서 전화번호 입력 시 올바른 전화번호 형식(정규식 `^01[0-9]-?[0-9]{3,4}-?[0-9]{4}$`)인지 서버 사이드 검증을 필수로 수행하여 오발송을 방지합니다.