lookupTimezone($ip); // 캐시 저장 (조회 실패 시에도 빈 문자열 저장하여 반복 조회 방지) if (config('geoip.cache.enabled', true)) { $ttl = config('geoip.cache.ttl', 86400); Cache::put($cacheKey, $timezone ?? '', $ttl); } // 지원 타임존 확인 if ($timezone && in_array($timezone, $supportedTimezones)) { return $timezone; } return null; } /** * MaxMind DB에서 타임존을 조회합니다. */ private function lookupTimezone(string $ip): ?string { try { $reader = $this->getReader(); if ($reader === null) { return null; } $record = $reader->city($ip); return $record->location->timeZone; } catch (AddressNotFoundException $e) { // IP를 찾을 수 없음 (정상적인 케이스) return null; } catch (\Exception $e) { Log::warning('GeoIP lookup failed', [ 'ip' => $ip, 'error' => $e->getMessage(), ]); return null; } } /** * MaxMind Reader 인스턴스를 반환합니다. */ private function getReader(): ?Reader { if ($this->reader !== null) { return $this->reader; } $dbPath = config('geoip.database_path'); if (! file_exists($dbPath)) { return null; } try { $this->reader = new Reader($dbPath); return $this->reader; } catch (\Exception $e) { Log::error('Failed to initialize GeoIP reader', [ 'path' => $dbPath, 'error' => $e->getMessage(), ]); return null; } } /** * GeoIP 데이터베이스가 사용 가능한지 확인합니다. */ public function isAvailable(): bool { if (! config('geoip.enabled', false)) { return false; } return file_exists(config('geoip.database_path')); } }