# GeoIP 시스템 (MaxMind GeoLite2) > IP 주소 기반 타임존 자동 감지 시스템 --- ## TL;DR (5초 요약) ```text 1. MaxMind GeoLite2-City DB 기반 IP → 타임존 감지 (SetTimezone 미들웨어 3단계) 2. 관리자 > 환경설정 > 고급 탭에서 마스터 토글 + 라이선스 키 관리 3. geoip:update 커맨드로 DB 다운로드 (주 1회 자동 스케줄) 4. 마스터 OFF 시 미들웨어 short-circuit — GeoIP 조회 완전 스킵 5. MaxMind 라이선스: 재배포 금지 → 각 환경에서 직접 다운로드 필수 ``` --- ## 아키텍처 ```text [관리자 고급 탭] ├── 마스터 토글 (geoip.feature_enabled) ├── 라이선스 키 (geoip.license_key) ├── 자동 업데이트 토글 (geoip.auto_update_enabled) └── 수동 업데이트 버튼 → POST /api/admin/settings/geoip/update [스케줄러] routes/console.php └── 주 1회(수요일 03:00) geoip:update 실행 └── 3개 조건 AND 게이트: 마스터 ON + 자동 업데이트 ON + 키 존재 [다운로드 흐름] GeoIpDatabaseService └── MaxMind API → tar.gz → PharData 추출 → 원자적 교체 → 메타데이터 갱신 [요청 처리] SetTimezone 미들웨어 └── 1.유저 설정 → 2.X-Timezone 헤더 → 3.GeoIP(마스터 ON일 때만) → 4.기본값 ``` ## 설정 카테고리 GeoIP 기능 설정은 독립 카테고리 `geoip`로 관리됩니다 (`merge_into: "advanced"`로 고급 탭에 병합). | 저장 키 | 프론트 키 | 타입 | 기본값 | 설명 | |---------|----------|------|--------|------| | `geoip.feature_enabled` | `advanced.geoip_enabled` | boolean | `false` | 마스터 스위치 | | `geoip.license_key` | `advanced.geoip_license_key` | string (sensitive) | `""` | MaxMind 라이선스 키 | | `geoip.auto_update_enabled` | `advanced.geoip_auto_update_enabled` | boolean | `true` | 자동 업데이트 활성화 | | `geoip.last_updated_at` | `advanced.geoip_last_updated_at` | string | `""` | 마지막 성공 다운로드 시각 (ISO8601) | **주의**: 기존 `cache.geoip_enabled` / `cache.geoip_ttl`은 **GeoIP 조회 결과 캐시** 설정으로 완전히 별개. ## 파일 구조 | 파일 | 역할 | |------|------| | `app/Services/GeoIpDatabaseService.php` | 다운로드/추출/교체/상태 조회 + 훅 체인 | | `app/Services/GeoIpService.php` | IP → 타임존 조회 (Reader 사용) | | `app/Console/Commands/GeoIp/UpdateGeoLiteDatabaseCommand.php` | `geoip:update` Artisan 커맨드 | | `app/Http/Controllers/Api/Admin/GeoIpController.php` | 수동 업데이트 API 트리거 | | `config/geoip.php` | GeoIP 전체 설정 (활성화, 키, DB 경로, 다운로드, 스케줄, 캐시) | | `config/settings/defaults.json` | `geoip` 카테고리 스키마 + 기본값 | | `storage/app/geoip/GeoLite2-City.mmdb` | MaxMind DB 파일 (`.gitignore` 제외) | ## Artisan 커맨드 ```bash # DB 다운로드 (라이선스 키 필요) php artisan geoip:update # 강제 재다운로드 (24시간 이내라도) php artisan geoip:update --force # URL/키 검증만 (실제 다운로드 X) php artisan geoip:update --dry-run # 스케줄 등록 확인 php artisan schedule:list | grep geoip ``` ## 훅 시스템 `GeoIpDatabaseService`는 다운로드 전/후에 훅을 실행합니다: | 훅 | 타입 | 시점 | |----|------|------| | `core.geoip.database.before_update` | Action | 다운로드 시작 전 | | `core.geoip.database.filter_download_context` | Filter | 다운로드 옵션 변형 (edition_id, base_url, timeout 등) | | `core.geoip.database.after_update` | Action | 다운로드 성공 후 | | `core.geoip.database.after_update_failed` | Action | 다운로드 실패 후 | ## 미들웨어 short-circuit `SetTimezone::determineTimezone()` 3단계(GeoIP)는 마스터 스위치가 OFF이면 완전히 스킵됩니다: ```php if ((bool) g7_core_settings('geoip.feature_enabled', false)) { $geoTimezone = $this->geoIpService->getTimezoneByIp(...); // ... } ``` - `g7_core_settings()` 헬퍼로 소스 오브 트루스 직접 참조 - `config('geoip.enabled')` 대신 사용 — `SettingsServiceProvider` 파이프라인 실패에 방어적 - 기본값 `false` — 최초 설치 직후 안전 비활성 ## MaxMind 라이선스 - **무료 계정**: https://www.maxmind.com/en/geolite2/signup - **재배포 금지**: mmdb 파일을 Git에 포함하거나 배포 패키지에 동봉할 수 없음 - `.gitignore`에 `storage/app/geoip/` 추가됨 - 각 운영 환경에서 `geoip:update`로 직접 다운로드해야 함 ## 다운로드 엔드포인트 ```text https://download.maxmind.com/app/geoip_download ?edition_id=GeoLite2-City &license_key={key} &suffix=tar.gz ``` - 파일 형식: `.tar.gz` 내부에 `GeoLite2-City_YYYYMMDD/GeoLite2-City.mmdb` - MaxMind 갱신 주기: 주 2회 (화/금) - G7 기본 스케줄: 주 1회 (수요일 03:00) ## 검증 규칙 `SaveSettingsRequest`에서 3개 필드 검증: ```php 'advanced.geoip_enabled' => ['nullable', 'boolean'], 'advanced.geoip_license_key' => ['nullable', 'string', 'max:200', 'regex:/^[A-Za-z0-9_]+$/'], 'advanced.geoip_auto_update_enabled' => ['nullable', 'boolean'], ``` - boolean 필드는 `prepareForValidation()`에서 `filter_var(FILTER_VALIDATE_BOOLEAN)` 전처리 - 라이선스 키: `regex` 영숫자/언더스코어만 (SSRF/삽입 방어)