@uniai-fe/uds-templates 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -35,6 +35,14 @@ const hasQueryValue = (value: unknown): boolean =>
35
35
  * @param {number|string|null} [params.farm_idx] 농장 식별자
36
36
  * @param {number|null} [params.lat] 위도
37
37
  * @param {number|null} [params.lng] 경도
38
+ * @param {number|null} [params.nx] backend 격자 X
39
+ * @param {number|null} [params.ny] backend 격자 Y
40
+ * @param {string} [params.base_date] 현재/예보 단일 route 재현용 날짜
41
+ * @param {string} [params.base_time] 현재/예보 단일 route 재현용 시각
42
+ * @param {string} [params.now_base_date] summary 현재 날씨 재현용 날짜
43
+ * @param {string} [params.now_base_time] summary 현재 날씨 재현용 시각
44
+ * @param {string} [params.forecast_base_date] summary 예보 날씨 재현용 날짜
45
+ * @param {string} [params.forecast_base_time] summary 예보 날씨 재현용 시각
38
46
  * @param {string} [params.locale] 요청 언어 코드
39
47
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
40
48
  * @return {API_Req_Weather} local route query 파라미터
@@ -43,12 +51,28 @@ export const getWeatherClientParams = ({
43
51
  farm_idx,
44
52
  lat,
45
53
  lng,
54
+ nx,
55
+ ny,
56
+ base_date,
57
+ base_time,
58
+ now_base_date,
59
+ now_base_time,
60
+ forecast_base_date,
61
+ forecast_base_time,
46
62
  locale,
47
63
  include_raw,
48
64
  }: API_Req_Weather): API_Req_Weather => ({
49
65
  ...(hasQueryValue(farm_idx) ? { farm_idx } : {}),
50
66
  ...(hasQueryValue(lat) ? { lat } : {}),
51
67
  ...(hasQueryValue(lng) ? { lng } : {}),
68
+ ...(hasQueryValue(nx) ? { nx } : {}),
69
+ ...(hasQueryValue(ny) ? { ny } : {}),
70
+ ...(hasQueryValue(base_date) ? { base_date } : {}),
71
+ ...(hasQueryValue(base_time) ? { base_time } : {}),
72
+ ...(hasQueryValue(now_base_date) ? { now_base_date } : {}),
73
+ ...(hasQueryValue(now_base_time) ? { now_base_time } : {}),
74
+ ...(hasQueryValue(forecast_base_date) ? { forecast_base_date } : {}),
75
+ ...(hasQueryValue(forecast_base_time) ? { forecast_base_time } : {}),
52
76
  ...(locale ? { locale } : {}),
53
77
  ...(typeof include_raw === "boolean" ? { include_raw } : {}),
54
78
  });
@@ -65,10 +89,12 @@ const getWeatherRoutePath = (
65
89
  ): string => routePath || fallbackRoutePath;
66
90
 
67
91
  /**
68
- * Weather Client; 현재/예보/summary 조회가 가능한 위치 입력인지 확인.
92
+ * Weather Client; 현재/예보 조회가 가능한 위치 입력인지 확인.
69
93
  * @param {API_Req_Weather} params 날씨 요청 파라미터
70
94
  * @param {number|null} [params.lat] 위도
71
95
  * @param {number|null} [params.lng] 경도
96
+ * @param {number|null} [params.nx] backend 격자 X
97
+ * @param {number|null} [params.ny] backend 격자 Y
72
98
  * @return {boolean} 위치 기반 weather query 실행 가능 여부
73
99
  */
74
100
  const isWeatherLocationAvailable = ({ lat, lng }: API_Req_Weather): boolean =>
@@ -79,6 +105,23 @@ const isWeatherLocationAvailable = ({ lat, lng }: API_Req_Weather): boolean =>
79
105
  isValidNumber(lat) &&
80
106
  isValidNumber(lng);
81
107
 
108
+ /**
109
+ * Weather Client; backend 격자 입력이 사용 가능한지 확인.
110
+ * @param {API_Req_Weather} params 날씨 요청 파라미터
111
+ * @param {number|null} [params.nx] backend 격자 X
112
+ * @param {number|null} [params.ny] backend 격자 Y
113
+ * @return {boolean} 격자 기반 weather query 실행 가능 여부
114
+ */
115
+ const isWeatherGridAvailable = ({ nx, ny }: API_Req_Weather): boolean =>
116
+ nx !== null &&
117
+ ny !== null &&
118
+ typeof nx !== "undefined" &&
119
+ typeof ny !== "undefined" &&
120
+ isValidNumber(nx) &&
121
+ isValidNumber(ny) &&
122
+ Number(nx) > 0 &&
123
+ Number(ny) > 0;
124
+
82
125
  /**
83
126
  * Weather Client; 특보 조회가 가능한 농장 식별자인지 확인.
84
127
  * @param {API_Req_WeatherAlert} params 특보 요청 파라미터
@@ -88,21 +131,66 @@ const isWeatherLocationAvailable = ({ lat, lng }: API_Req_Weather): boolean =>
88
131
  const isWeatherFarmAvailable = ({ farm_idx }: API_Req_WeatherAlert): boolean =>
89
132
  hasQueryValue(farm_idx) && Number(farm_idx) > 0;
90
133
 
134
+ /**
135
+ * Weather Client; summary 조회에 필요한 입력인지 확인.
136
+ * @param {API_Req_Weather} params summary 요청 파라미터
137
+ * @param {number|string|null} [params.farm_idx] 농장 식별자
138
+ * @return {boolean} summary query 실행 가능 여부
139
+ */
140
+ const isWeatherSummaryAvailable = (params: API_Req_Weather): boolean =>
141
+ isWeatherFarmAvailable({ farm_idx: params.farm_idx ?? null }) &&
142
+ (isWeatherLocationAvailable(params) || isWeatherGridAvailable(params));
143
+
91
144
  /**
92
145
  * Weather Client; React Query key에 사용할 weather 요청 값을 정렬.
93
146
  * @param {API_Req_Weather} params 날씨 요청 파라미터
94
147
  * @param {number|string|null} [params.farm_idx] 농장 식별자
95
148
  * @param {number|null} [params.lat] 위도
96
149
  * @param {number|null} [params.lng] 경도
150
+ * @param {number|null} [params.nx] backend 격자 X
151
+ * @param {number|null} [params.ny] backend 격자 Y
152
+ * @param {string} [params.base_date] 현재/예보 단일 route 재현용 날짜
153
+ * @param {string} [params.base_time] 현재/예보 단일 route 재현용 시각
154
+ * @param {string} [params.now_base_date] summary 현재 날씨 재현용 날짜
155
+ * @param {string} [params.now_base_time] summary 현재 날씨 재현용 시각
156
+ * @param {string} [params.forecast_base_date] summary 예보 날씨 재현용 날짜
157
+ * @param {string} [params.forecast_base_time] summary 예보 날씨 재현용 시각
97
158
  * @param {string} [params.locale] 요청 언어 코드
98
159
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
99
160
  * @return {readonly unknown[]} React Query key 값
100
161
  */
101
162
  const getWeatherQueryKeyValues = (params: API_Req_Weather) => {
102
- const { farm_idx, lat, lng, locale, include_raw } =
103
- getWeatherClientParams(params);
163
+ const {
164
+ farm_idx,
165
+ lat,
166
+ lng,
167
+ nx,
168
+ ny,
169
+ base_date,
170
+ base_time,
171
+ now_base_date,
172
+ now_base_time,
173
+ forecast_base_date,
174
+ forecast_base_time,
175
+ locale,
176
+ include_raw,
177
+ } = getWeatherClientParams(params);
104
178
 
105
- return [farm_idx, lat, lng, locale, include_raw] as const;
179
+ return [
180
+ farm_idx,
181
+ lat,
182
+ lng,
183
+ nx,
184
+ ny,
185
+ base_date,
186
+ base_time,
187
+ now_base_date,
188
+ now_base_time,
189
+ forecast_base_date,
190
+ forecast_base_time,
191
+ locale,
192
+ include_raw,
193
+ ] as const;
106
194
  };
107
195
 
108
196
  /**
@@ -112,6 +200,10 @@ const getWeatherQueryKeyValues = (params: API_Req_Weather) => {
112
200
  * @param {number|string|null} [params.farm_idx] 농장 식별자
113
201
  * @param {number|null} [params.lat] 위도
114
202
  * @param {number|null} [params.lng] 경도
203
+ * @param {number|null} [params.nx] backend 격자 X
204
+ * @param {number|null} [params.ny] backend 격자 Y
205
+ * @param {string} [params.base_date] 현재 날씨 재현용 날짜
206
+ * @param {string} [params.base_time] 현재 날씨 재현용 시각
115
207
  * @param {string} [params.locale] 요청 언어 코드
116
208
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
117
209
  * @param {WeatherClientRouteOptions} [options] local route 옵션
@@ -138,6 +230,10 @@ export const getWeatherNow = async <Data = API_Res_WeatherNow>(
138
230
  * @param {number|string|null} [params.farm_idx] 농장 식별자
139
231
  * @param {number|null} [params.lat] 위도
140
232
  * @param {number|null} [params.lng] 경도
233
+ * @param {number|null} [params.nx] backend 격자 X
234
+ * @param {number|null} [params.ny] backend 격자 Y
235
+ * @param {string} [params.base_date] 예보 날씨 재현용 날짜
236
+ * @param {string} [params.base_time] 예보 날씨 재현용 시각
141
237
  * @param {string} [params.locale] 요청 언어 코드
142
238
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
143
239
  * @param {WeatherClientRouteOptions} [options] local route 옵션
@@ -164,6 +260,12 @@ export const getWeatherForecast = async <Data = API_Res_WeatherForecast>(
164
260
  * @param {number|string|null} [params.farm_idx] 농장 식별자
165
261
  * @param {number|null} [params.lat] 위도
166
262
  * @param {number|null} [params.lng] 경도
263
+ * @param {number|null} [params.nx] backend 격자 X
264
+ * @param {number|null} [params.ny] backend 격자 Y
265
+ * @param {string} [params.now_base_date] summary 현재 날씨 재현용 날짜
266
+ * @param {string} [params.now_base_time] summary 현재 날씨 재현용 시각
267
+ * @param {string} [params.forecast_base_date] summary 예보 날씨 재현용 날짜
268
+ * @param {string} [params.forecast_base_time] summary 예보 날씨 재현용 시각
167
269
  * @param {string} [params.locale] 요청 언어 코드
168
270
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
169
271
  * @param {WeatherClientRouteOptions} [options] local route 옵션
@@ -212,6 +314,10 @@ export const getWeatherAlert = async <Data = API_Res_WeatherAlert>(
212
314
  * @param {number|string|null} [params.farm_idx] 농장 식별자
213
315
  * @param {number|null} [params.lat] 위도
214
316
  * @param {number|null} [params.lng] 경도
317
+ * @param {number|null} [params.nx] backend 격자 X
318
+ * @param {number|null} [params.ny] backend 격자 Y
319
+ * @param {string} [params.base_date] 현재 날씨 재현용 날짜
320
+ * @param {string} [params.base_time] 현재 날씨 재현용 시각
215
321
  * @param {string} [params.locale] 요청 언어 코드
216
322
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
217
323
  * @param {WeatherClientQueryOptions} [options] query 옵션
@@ -231,7 +337,9 @@ export const useQueryWeatherNow = <Data = API_Res_WeatherNow>(
231
337
  return useQuery({
232
338
  queryKey: ["weather_now", routePath, ...getWeatherQueryKeyValues(params)],
233
339
  queryFn: () => getWeatherNow<Data>(params, { routePath }),
234
- enabled: (options.enabled ?? true) && isWeatherLocationAvailable(params),
340
+ enabled:
341
+ (options.enabled ?? true) &&
342
+ (isWeatherLocationAvailable(params) || isWeatherGridAvailable(params)),
235
343
  staleTime: 10 * 60 * 1000,
236
344
  refetchInterval: 5 * 60 * 1000,
237
345
  refetchOnWindowFocus: true,
@@ -245,6 +353,10 @@ export const useQueryWeatherNow = <Data = API_Res_WeatherNow>(
245
353
  * @param {number|string|null} [params.farm_idx] 농장 식별자
246
354
  * @param {number|null} [params.lat] 위도
247
355
  * @param {number|null} [params.lng] 경도
356
+ * @param {number|null} [params.nx] backend 격자 X
357
+ * @param {number|null} [params.ny] backend 격자 Y
358
+ * @param {string} [params.base_date] 예보 날씨 재현용 날짜
359
+ * @param {string} [params.base_time] 예보 날씨 재현용 시각
248
360
  * @param {string} [params.locale] 요청 언어 코드
249
361
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
250
362
  * @param {WeatherClientQueryOptions} [options] query 옵션
@@ -268,7 +380,9 @@ export const useQueryWeatherForecast = <Data = API_Res_WeatherForecast>(
268
380
  ...getWeatherQueryKeyValues(params),
269
381
  ],
270
382
  queryFn: () => getWeatherForecast<Data>(params, { routePath }),
271
- enabled: (options.enabled ?? true) && isWeatherLocationAvailable(params),
383
+ enabled:
384
+ (options.enabled ?? true) &&
385
+ (isWeatherLocationAvailable(params) || isWeatherGridAvailable(params)),
272
386
  staleTime: 30 * 60 * 1000,
273
387
  });
274
388
  };
@@ -280,6 +394,12 @@ export const useQueryWeatherForecast = <Data = API_Res_WeatherForecast>(
280
394
  * @param {number|string|null} [params.farm_idx] 농장 식별자
281
395
  * @param {number|null} [params.lat] 위도
282
396
  * @param {number|null} [params.lng] 경도
397
+ * @param {number|null} [params.nx] backend 격자 X
398
+ * @param {number|null} [params.ny] backend 격자 Y
399
+ * @param {string} [params.now_base_date] summary 현재 날씨 재현용 날짜
400
+ * @param {string} [params.now_base_time] summary 현재 날씨 재현용 시각
401
+ * @param {string} [params.forecast_base_date] summary 예보 날씨 재현용 날짜
402
+ * @param {string} [params.forecast_base_time] summary 예보 날씨 재현용 시각
283
403
  * @param {string} [params.locale] 요청 언어 코드
284
404
  * @param {boolean} [params.include_raw] 원본 응답 포함 여부
285
405
  * @param {WeatherClientQueryOptions} [options] query 옵션
@@ -303,7 +423,7 @@ export const useQueryWeatherSummary = <Data = API_Res_WeatherSummary>(
303
423
  ...getWeatherQueryKeyValues(params),
304
424
  ],
305
425
  queryFn: () => getWeatherSummary<Data>(params, { routePath }),
306
- enabled: (options.enabled ?? true) && isWeatherLocationAvailable(params),
426
+ enabled: (options.enabled ?? true) && isWeatherSummaryAvailable(params),
307
427
  staleTime: 10 * 60 * 1000,
308
428
  });
309
429
  };
@@ -4,6 +4,7 @@ import { generateQueryUrl, nextAPILog } from "@uniai-fe/util-api";
4
4
  import { setDebugResponseHeaders } from "@uniai-fe/util-next";
5
5
  import type { NextResponse } from "next/server";
6
6
  import WEATHER_RESPONSE from "../data/response";
7
+ import { convertWeatherCoordinateToGrid } from "../utils";
7
8
  import type {
8
9
  API_Res_WeatherAlert,
9
10
  API_Res_WeatherForecast,
@@ -23,34 +24,136 @@ const QUERY_URL = {
23
24
  };
24
25
 
25
26
  const { API_RES_BASE } = WEATHER_RESPONSE;
26
- const BACKEND_QUERY_KEYS = [
27
+ type WeatherBackendRouteKind = keyof typeof QUERY_URL;
28
+
29
+ const BACKEND_COMMON_QUERY_KEYS = [
27
30
  "farm_idx",
28
31
  "lat",
29
32
  "lng",
30
33
  "locale",
31
34
  "include_raw",
32
35
  ] as const;
36
+ const BACKEND_DATE_TIME_QUERY_PAIRS = {
37
+ now: [["base_date", "base_time"]],
38
+ forecast: [["base_date", "base_time"]],
39
+ summary: [
40
+ ["now_base_date", "now_base_time"],
41
+ ["forecast_base_date", "forecast_base_time"],
42
+ ],
43
+ alert: [],
44
+ } as const;
45
+
46
+ /**
47
+ * Weather backend; query 값 존재 여부 확인.
48
+ * @param {string | null} value 확인할 query 값
49
+ * @return {boolean} query 값 존재 여부
50
+ */
51
+ const hasSearchParamValue = (value: string | null): value is string =>
52
+ value !== null && value !== "";
53
+
54
+ /**
55
+ * Weather backend; query 값을 숫자로 변환한다.
56
+ * @param {URLSearchParams} searchParams service app local route query
57
+ * @param {string} key 숫자로 읽을 query key
58
+ * @return {number | null} 변환된 숫자 또는 null
59
+ */
60
+ const getSearchParamNumber = (
61
+ searchParams: URLSearchParams,
62
+ key: string,
63
+ ): number | null => {
64
+ const value = searchParams.get(key);
65
+
66
+ if (!hasSearchParamValue(value)) return null;
67
+
68
+ const parsed = Number(value);
69
+
70
+ return Number.isFinite(parsed) ? parsed : null;
71
+ };
72
+
73
+ /**
74
+ * Weather backend; 재현용 date/time query를 pair 단위로 전달한다.
75
+ * @param {URLSearchParams} params backend 전달 query
76
+ * @param {URLSearchParams} searchParams service app local route query
77
+ * @param {WeatherBackendRouteKind} routeKind backend weather route 종류
78
+ */
79
+ const setBackendDateTimeSearchParams = (
80
+ params: URLSearchParams,
81
+ searchParams: URLSearchParams,
82
+ routeKind: WeatherBackendRouteKind,
83
+ ): void => {
84
+ BACKEND_DATE_TIME_QUERY_PAIRS[routeKind].forEach(([dateKey, timeKey]) => {
85
+ const date = searchParams.get(dateKey);
86
+ const time = searchParams.get(timeKey);
87
+
88
+ if (!hasSearchParamValue(date) || !hasSearchParamValue(time)) return;
89
+
90
+ params.set(dateKey, date);
91
+ params.set(timeKey, time);
92
+ });
93
+ };
94
+
95
+ /**
96
+ * Weather backend; nx/ny query를 직접 값 또는 lat/lng 변환값으로 전달한다.
97
+ * @param {URLSearchParams} params backend 전달 query
98
+ * @param {URLSearchParams} searchParams service app local route query
99
+ */
100
+ const setBackendGridSearchParams = (
101
+ params: URLSearchParams,
102
+ searchParams: URLSearchParams,
103
+ ): void => {
104
+ const providedNx = getSearchParamNumber(searchParams, "nx");
105
+ const providedNy = getSearchParamNumber(searchParams, "ny");
106
+ const { nx, ny } =
107
+ providedNx !== null && providedNy !== null
108
+ ? { nx: providedNx, ny: providedNy }
109
+ : convertWeatherCoordinateToGrid({
110
+ lat: getSearchParamNumber(searchParams, "lat"),
111
+ lng: getSearchParamNumber(searchParams, "lng"),
112
+ });
113
+
114
+ if (nx !== null && nx > 0) params.set("nx", String(nx));
115
+ if (ny !== null && ny > 0) params.set("ny", String(ny));
116
+ };
33
117
 
34
118
  /**
35
119
  * Weather backend; client route query를 backend 허용 query로 정리.
120
+ * @param {WeatherBackendRouteKind} routeKind backend weather route 종류
36
121
  * @param {URLSearchParams} searchParams service app local route query
37
122
  * @param {string} [searchParams.farm_idx] 농장 식별자
38
123
  * @param {string} [searchParams.lat] 위도
39
124
  * @param {string} [searchParams.lng] 경도
125
+ * @param {string} [searchParams.nx] backend 격자 X
126
+ * @param {string} [searchParams.ny] backend 격자 Y
127
+ * @param {string} [searchParams.base_date] 현재/예보 단일 route 재현용 날짜
128
+ * @param {string} [searchParams.base_time] 현재/예보 단일 route 재현용 시각
129
+ * @param {string} [searchParams.now_base_date] summary 현재 날씨 재현용 날짜
130
+ * @param {string} [searchParams.now_base_time] summary 현재 날씨 재현용 시각
131
+ * @param {string} [searchParams.forecast_base_date] summary 예보 날씨 재현용 날짜
132
+ * @param {string} [searchParams.forecast_base_time] summary 예보 날씨 재현용 시각
40
133
  * @param {string} [searchParams.locale] 요청 언어 코드
41
134
  * @param {string} [searchParams.include_raw] 원본 응답 포함 여부
42
135
  * @return {URLSearchParams} backend 전달 query
43
136
  */
44
137
  const getBackendSearchParams = (
138
+ routeKind: WeatherBackendRouteKind,
45
139
  searchParams: URLSearchParams,
46
140
  ): URLSearchParams => {
47
141
  const params = new URLSearchParams();
48
142
 
49
- BACKEND_QUERY_KEYS.forEach(key => {
143
+ if (routeKind === "alert") {
144
+ const farmIdx = searchParams.get("farm_idx");
145
+ if (hasSearchParamValue(farmIdx)) params.set("farm_idx", farmIdx);
146
+ return params;
147
+ }
148
+
149
+ BACKEND_COMMON_QUERY_KEYS.forEach(key => {
50
150
  const value = searchParams.get(key);
51
- if (value !== null && value !== "") params.set(key, value);
151
+ if (hasSearchParamValue(value)) params.set(key, value);
52
152
  });
53
153
 
154
+ setBackendGridSearchParams(params, searchParams);
155
+ setBackendDateTimeSearchParams(params, searchParams, routeKind);
156
+
54
157
  return params;
55
158
  };
56
159
 
@@ -124,6 +227,7 @@ const getWeatherSummaryDefault = (): API_Res_WeatherSummary => ({
124
227
  * @param {string} params.domain backend API domain
125
228
  * @param {string} params.routeUrl local route URL
126
229
  * @param {string} params.queryUrl backend weather route URL
230
+ * @param {"now"|"forecast"|"summary"|"alert"} params.routeKind backend weather route 종류
127
231
  * @param {URLSearchParams} params.searchParams client route query
128
232
  * @param {Data} params.fallback 실패 시 반환할 fallback 응답
129
233
  * @return {Promise<Data>} backend 응답 또는 fallback
@@ -132,6 +236,7 @@ const fetchWeatherBackend = async <Data>({
132
236
  domain,
133
237
  routeUrl,
134
238
  queryUrl,
239
+ routeKind,
135
240
  searchParams,
136
241
  fallback,
137
242
  }: WeatherBackendFetchParams<Data>): Promise<Data> => {
@@ -142,7 +247,7 @@ const fetchWeatherBackend = async <Data>({
142
247
  return fallback;
143
248
  }
144
249
 
145
- const backendParams = getBackendSearchParams(searchParams);
250
+ const backendParams = getBackendSearchParams(routeKind, searchParams);
146
251
  const url = generateQueryUrl({
147
252
  domain,
148
253
  routeUrl,
@@ -185,6 +290,7 @@ export const routeWeatherNow = async ({
185
290
  domain,
186
291
  routeUrl,
187
292
  queryUrl: QUERY_URL.now,
293
+ routeKind: "now",
188
294
  searchParams,
189
295
  fallback: getWeatherNowDefault(),
190
296
  });
@@ -207,6 +313,7 @@ export const routeWeatherForecast = async ({
207
313
  domain,
208
314
  routeUrl,
209
315
  queryUrl: QUERY_URL.forecast,
316
+ routeKind: "forecast",
210
317
  searchParams,
211
318
  fallback: getWeatherForecastDefault(),
212
319
  });
@@ -229,6 +336,7 @@ export const routeWeatherSummary = async ({
229
336
  domain,
230
337
  routeUrl,
231
338
  queryUrl: QUERY_URL.summary,
339
+ routeKind: "summary",
232
340
  searchParams,
233
341
  fallback: getWeatherSummaryDefault(),
234
342
  });
@@ -251,6 +359,7 @@ export const routeWeatherAlert = async ({
251
359
  domain,
252
360
  routeUrl,
253
361
  queryUrl: QUERY_URL.alert,
362
+ routeKind: "alert",
254
363
  searchParams,
255
364
  fallback: getWeatherAlertDefault(),
256
365
  });
@@ -259,6 +368,6 @@ export const routeWeatherAlert = async ({
259
368
  res,
260
369
  domain,
261
370
  queryUrl: QUERY_URL.alert,
262
- searchParams: getBackendSearchParams(searchParams),
371
+ searchParams: getBackendSearchParams("alert", searchParams),
263
372
  });
264
373
  };
@@ -2,6 +2,7 @@ import type {
2
2
  WeatherApiLocaleOptions,
3
3
  WeatherCoordinate,
4
4
  WeatherGeoCoordinate,
5
+ WeatherGridCoordinate,
5
6
  } from "./base";
6
7
 
7
8
  /**
@@ -9,11 +10,22 @@ import type {
9
10
  * @property {number|string|null} [farm_idx] 농장 식별자
10
11
  * @property {number|null} [lat] 위도
11
12
  * @property {number|null} [lng] 경도
13
+ * @property {number|null} [nx] backend 격자 X
14
+ * @property {number|null} [ny] backend 격자 Y
15
+ * @property {string} [base_date] 현재/예보 단일 route 재현용 날짜
16
+ * @property {string} [base_time] 현재/예보 단일 route 재현용 시각
17
+ * @property {string} [now_base_date] summary 현재 날씨 재현용 날짜
18
+ * @property {string} [now_base_time] summary 현재 날씨 재현용 시각
19
+ * @property {string} [forecast_base_date] summary 예보 날씨 재현용 날짜
20
+ * @property {string} [forecast_base_time] summary 예보 날씨 재현용 시각
12
21
  * @property {string} [locale] 요청 언어 코드
13
22
  * @property {boolean} [include_raw] 원본 응답 포함 여부
14
23
  */
15
24
  export interface API_Req_Weather
16
- extends WeatherApiLocaleOptions, Partial<WeatherGeoCoordinate> {
25
+ extends
26
+ WeatherApiLocaleOptions,
27
+ Partial<WeatherGeoCoordinate>,
28
+ Partial<WeatherGridCoordinate> {
17
29
  /**
18
30
  * 농장 식별자
19
31
  */
@@ -26,6 +38,38 @@ export interface API_Req_Weather
26
38
  * 경도
27
39
  */
28
40
  lng?: number | null;
41
+ /**
42
+ * backend 격자 X
43
+ */
44
+ nx?: number | null;
45
+ /**
46
+ * backend 격자 Y
47
+ */
48
+ ny?: number | null;
49
+ /**
50
+ * 현재/예보 단일 route 재현용 날짜
51
+ */
52
+ base_date?: string;
53
+ /**
54
+ * 현재/예보 단일 route 재현용 시각
55
+ */
56
+ base_time?: string;
57
+ /**
58
+ * summary 현재 날씨 재현용 날짜
59
+ */
60
+ now_base_date?: string;
61
+ /**
62
+ * summary 현재 날씨 재현용 시각
63
+ */
64
+ now_base_time?: string;
65
+ /**
66
+ * summary 예보 날씨 재현용 날짜
67
+ */
68
+ forecast_base_date?: string;
69
+ /**
70
+ * summary 예보 날씨 재현용 시각
71
+ */
72
+ forecast_base_time?: string;
29
73
  /**
30
74
  * 요청 언어 코드
31
75
  */
@@ -128,6 +172,7 @@ export interface WeatherServerRouteParams {
128
172
  * @property {string} routeUrl service app local route URL
129
173
  * @property {URLSearchParams} searchParams service app local route query
130
174
  * @property {string} queryUrl backend weather route URL
175
+ * @property {"now"|"forecast"|"summary"|"alert"} routeKind backend weather route 종류
131
176
  * @property {Data} fallback 실패 시 반환할 fallback 응답
132
177
  */
133
178
  export type WeatherBackendFetchParams<Data> = WeatherServerRouteParams & {
@@ -135,6 +180,10 @@ export type WeatherBackendFetchParams<Data> = WeatherServerRouteParams & {
135
180
  * backend weather route URL
136
181
  */
137
182
  queryUrl: string;
183
+ /**
184
+ * backend weather route 종류
185
+ */
186
+ routeKind: "now" | "forecast" | "summary" | "alert";
138
187
  /**
139
188
  * 실패 시 반환할 fallback 응답
140
189
  */
@@ -30,6 +30,22 @@ export type WeatherGeoCoordinate = {
30
30
  lng: number | null;
31
31
  };
32
32
 
33
+ /**
34
+ * 날씨 backend에서 사용하는 격자 좌표.
35
+ * @property {number | null} nx 격자 X
36
+ * @property {number | null} ny 격자 Y
37
+ */
38
+ export type WeatherGridCoordinate = {
39
+ /**
40
+ * 격자 X
41
+ */
42
+ nx: number | null;
43
+ /**
44
+ * 격자 Y
45
+ */
46
+ ny: number | null;
47
+ };
48
+
33
49
  /**
34
50
  * 날씨 API locale 요청 옵션.
35
51
  * @property {string} [locale] 요청 언어 코드. 기본 지원값은 ko, en, ja이며 타입은 확장 가능한 string으로 유지한다.
@@ -1,4 +1,8 @@
1
- import type { GeoCoordinate } from "../types";
1
+ import type {
2
+ GeoCoordinate,
3
+ WeatherGeoCoordinate,
4
+ WeatherGridCoordinate,
5
+ } from "../types";
2
6
 
3
7
  /**
4
8
  * 날씨 도구; 접속위치 추출.
@@ -27,3 +31,79 @@ export const userLocation = async (): Promise<GeoCoordinate> => {
27
31
  return { latitude: null, longitude: null };
28
32
  }
29
33
  };
34
+
35
+ /**
36
+ * 날씨 도구; 위경도 좌표를 backend weather 격자 좌표로 변환한다.
37
+ * @util
38
+ * @param {GeoCoordinate} coordinate 위경도 좌표
39
+ * @param {number|null} coordinate.latitude 위도
40
+ * @param {number|null} coordinate.longitude 경도
41
+ * @return {WeatherGridCoordinate} backend weather 격자 좌표
42
+ */
43
+ export const convertCoordinateToGrid = ({
44
+ latitude,
45
+ longitude,
46
+ }: GeoCoordinate): WeatherGridCoordinate => {
47
+ const fallback: WeatherGridCoordinate = { nx: null, ny: null };
48
+
49
+ if (latitude === null || longitude === null) return fallback;
50
+
51
+ const RE = 6371.00877;
52
+ const GRID = 5.0;
53
+ const SLAT1 = 30.0;
54
+ const SLAT2 = 60.0;
55
+ const ORIGIN_LONGITUDE = 126.0;
56
+ const ORIGIN_LATITUDE = 38.0;
57
+ const XO = 43;
58
+ const YO = 136;
59
+
60
+ const RADIAN = Math.PI / 180.0;
61
+ const re = RE / GRID;
62
+ const slat1 = SLAT1 * RADIAN;
63
+ const slat2 = SLAT2 * RADIAN;
64
+ const originLongitude = ORIGIN_LONGITUDE * RADIAN;
65
+ const originLatitude = ORIGIN_LATITUDE * RADIAN;
66
+
67
+ const sn =
68
+ Math.log(Math.cos(slat1) / Math.cos(slat2)) /
69
+ Math.log(
70
+ Math.tan(Math.PI * 0.25 + slat2 * 0.5) /
71
+ Math.tan(Math.PI * 0.25 + slat1 * 0.5),
72
+ );
73
+
74
+ const sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
75
+ const sfPow = (Math.pow(sf, sn) * Math.cos(slat1)) / sn;
76
+ const ro =
77
+ (re * sfPow) /
78
+ Math.pow(Math.tan(Math.PI * 0.25 + originLatitude * 0.5), sn);
79
+ const ra =
80
+ (re * sfPow) /
81
+ Math.pow(Math.tan(Math.PI * 0.25 + latitude * RADIAN * 0.5), sn);
82
+ let theta = longitude * RADIAN - originLongitude;
83
+
84
+ if (theta > Math.PI) theta -= 2.0 * Math.PI;
85
+ if (theta < -Math.PI) theta += 2.0 * Math.PI;
86
+ theta *= sn;
87
+
88
+ return {
89
+ nx: Math.floor(ra * Math.sin(theta) + XO + 0.5),
90
+ ny: Math.floor(ro - ra * Math.cos(theta) + YO + 0.5),
91
+ };
92
+ };
93
+
94
+ /**
95
+ * 날씨 도구; weather 위경도 좌표를 backend weather 격자 좌표로 변환한다.
96
+ * @util
97
+ * @param {Partial<WeatherGeoCoordinate>} coordinate weather 위경도 좌표
98
+ * @param {number|null} [coordinate.lat] 위도
99
+ * @param {number|null} [coordinate.lng] 경도
100
+ * @return {WeatherGridCoordinate} backend weather 격자 좌표
101
+ */
102
+ export const convertWeatherCoordinateToGrid = ({
103
+ lat,
104
+ lng,
105
+ }: Partial<WeatherGeoCoordinate>): WeatherGridCoordinate =>
106
+ convertCoordinateToGrid({
107
+ latitude: lat ?? null,
108
+ longitude: lng ?? null,
109
+ });