From 14c9b787e98b4fe32b65ead813a9cccb8c8af102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sun, 22 Feb 2026 18:54:27 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[dashboard]=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=EB=82=A0=EC=94=A8=20=EC=A4=91=EA=B8=B0=EC=98=88=EB=B3=B4=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=88=84=EB=9D=BD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 18시 발표 중기예보 미발행 시 06시/전일 발표로 fallback - 불완전한 날씨 데이터 캐시 TTL 10분으로 단축 (기존 3시간) --- app/Services/WeatherService.php | 98 ++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/app/Services/WeatherService.php b/app/Services/WeatherService.php index 403d3684..ec26c4d3 100644 --- a/app/Services/WeatherService.php +++ b/app/Services/WeatherService.php @@ -33,7 +33,7 @@ public function __construct() } /** - * 7일 예보 데이터 반환 (3시간 캐시) + * 7일 예보 데이터 반환 (완전한 데이터 3시간, 불완전 10분 캐시) */ public function getWeeklyForecast(): array { @@ -41,15 +41,26 @@ public function getWeeklyForecast(): array return []; } - return Cache::remember('weather_forecast_7days', 10800, function () { - try { - return $this->buildForecast(); - } catch (\Throwable $e) { - Log::warning('WeatherService: 예보 조합 실패', ['error' => $e->getMessage()]); + $cached = Cache::get('weather_forecast_7days'); + if ($cached !== null) { + return $cached; + } - return []; - } - }); + try { + $forecasts = $this->buildForecast(); + } catch (\Throwable $e) { + Log::warning('WeatherService: 예보 조합 실패', ['error' => $e->getMessage()]); + + return []; + } + + // 데이터 없는 날이 있으면 10분만 캐시 (API 재시도 유도) + $hasIncomplete = collect($forecasts)->contains( + fn ($fc) => $fc['tmn'] === null && $fc['tmx'] === null && $fc['icon'] === null + ); + Cache::put('weather_forecast_7days', $forecasts, $hasIncomplete ? 600 : 10800); + + return $forecasts; } private function buildForecast(): array @@ -190,61 +201,72 @@ private function fetchShortForecast(): array } /** - * 중기기온 API 호출 + * 중기기온 API 호출 (18시 발표 실패 시 06시 fallback) */ private function fetchMidTemperature(): array { - $body = $this->callApi(self::BASE_MID_TA, [ - 'pageNo' => 1, - 'numOfRows' => 10, - 'regId' => self::MID_TA_REG_ID, - 'tmFc' => $this->getMidBaseTmFc(), - ]); + $tmFcList = $this->getMidBaseTmFcCandidates(); - if (! $body) { - return []; + foreach ($tmFcList as $tmFc) { + $body = $this->callApi(self::BASE_MID_TA, [ + 'pageNo' => 1, + 'numOfRows' => 10, + 'regId' => self::MID_TA_REG_ID, + 'tmFc' => $tmFc, + ]); + + $items = $body ? data_get($body, 'response.body.items.item', []) : []; + if (! empty($items)) { + return $items[0]; + } } - $items = data_get($body, 'response.body.items.item', []); - - return $items[0] ?? []; + return []; } /** - * 중기날씨 API 호출 + * 중기날씨 API 호출 (18시 발표 실패 시 06시 fallback) */ private function fetchMidLandForecast(): array { - $body = $this->callApi(self::BASE_MID_LAND, [ - 'pageNo' => 1, - 'numOfRows' => 10, - 'regId' => self::MID_LAND_REG_ID, - 'tmFc' => $this->getMidBaseTmFc(), - ]); + $tmFcList = $this->getMidBaseTmFcCandidates(); - if (! $body) { - return []; + foreach ($tmFcList as $tmFc) { + $body = $this->callApi(self::BASE_MID_LAND, [ + 'pageNo' => 1, + 'numOfRows' => 10, + 'regId' => self::MID_LAND_REG_ID, + 'tmFc' => $tmFc, + ]); + + $items = $body ? data_get($body, 'response.body.items.item', []) : []; + if (! empty($items)) { + return $items[0]; + } } - $items = data_get($body, 'response.body.items.item', []); - - return $items[0] ?? []; + return []; } /** - * 중기예보 발표시각 (06시/18시 기준) + * 중기예보 발표시각 후보 목록 (우선순위순, fallback 포함) */ - private function getMidBaseTmFc(): string + private function getMidBaseTmFcCandidates(): array { $now = Carbon::now(); + $today = $now->format('Ymd'); + $yesterday = $now->copy()->subDay()->format('Ymd'); if ($now->hour >= 18) { - return $now->format('Ymd').'1800'; + // 18시 발표 → 06시 발표 → 전일 18시 발표 + return [$today.'1800', $today.'0600', $yesterday.'1800']; } elseif ($now->hour >= 6) { - return $now->format('Ymd').'0600'; + // 06시 발표 → 전일 18시 발표 + return [$today.'0600', $yesterday.'1800']; } - return $now->copy()->subDay()->format('Ymd').'1800'; + // 자정~06시: 전일 18시 발표 → 전일 06시 발표 + return [$yesterday.'1800', $yesterday.'0600']; } /**