@uniai-fe/uds-templates 0.5.29 → 0.6.0
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/README.md +3 -6
- package/package.json +1 -1
- package/src/weather/_legacy/apis/index.ts +4 -0
- package/src/weather/_legacy/data/response.ts +36 -0
- package/src/weather/_legacy/hooks/index.ts +5 -0
- package/src/weather/{hooks → _legacy/hooks}/useOpenWeatherMap.ts +1 -1
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKorea.ts +4 -7
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKoreaAlert.ts +2 -4
- package/src/weather/_legacy/types/api.ts +221 -0
- package/src/weather/_legacy/types/base.ts +70 -0
- package/src/weather/_legacy/types/index.ts +4 -0
- package/src/weather/_legacy/utils/index.ts +5 -0
- package/src/weather/_legacy/utils/locale.ts +28 -0
- package/src/weather/_legacy/utils/location.ts +139 -0
- package/src/weather/_legacy/utils/weather.ts +460 -0
- package/src/weather/apis/client.ts +339 -0
- package/src/weather/apis/index.ts +2 -4
- package/src/weather/apis/server.ts +264 -0
- package/src/weather/components/icon/Address.tsx +7 -0
- package/src/weather/components/icon/Weather.tsx +7 -6
- package/src/weather/components/page-header/Address.tsx +14 -0
- package/src/weather/components/page-header/Alert.tsx +17 -13
- package/src/weather/components/page-header/Container.tsx +12 -19
- package/src/weather/components/page-header/Forecast.tsx +21 -28
- package/src/weather/components/page-header/NextDays.tsx +10 -0
- package/src/weather/components/page-header/Today.tsx +86 -158
- package/src/weather/components/page-header/index.ts +5 -0
- package/src/weather/data/response.ts +4 -23
- package/src/weather/hooks/index.ts +3 -3
- package/src/weather/hooks/useWeather.ts +52 -0
- package/src/weather/hooks/useWeatherAlert.ts +35 -0
- package/src/weather/index.tsx +2 -2
- package/src/weather/jotai/coordinate.ts +4 -0
- package/src/weather/jotai/farm-idx.ts +4 -0
- package/src/weather/types/api.ts +393 -114
- package/src/weather/types/base.ts +15 -32
- package/src/weather/types/index.ts +0 -3
- package/src/weather/types/page-header.ts +118 -68
- package/src/weather/utils/index.ts +6 -4
- package/src/weather/utils/locale.ts +7 -69
- package/src/weather/utils/location.ts +6 -141
- package/src/weather/utils/weather.ts +53 -456
- package/src/weather/data/alert-regions-meta.json +0 -1286
- package/src/weather/data/weather-regions-meta.json +0 -9833
- package/src/weather/types/provider.ts +0 -34
- package/src/weather/utils/alert.ts +0 -30
- /package/src/weather/{apis → _legacy/apis}/korea/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/korea/server.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/server.ts +0 -0
- /package/src/weather/{types → _legacy/types}/korea.ts +0 -0
- /package/src/weather/{types → _legacy/types}/open-weather-map.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/date-time.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/validate.ts +0 -0
package/README.md
CHANGED
|
@@ -57,15 +57,12 @@ Next.js 서비스에서 primitives와 동일한 방식으로 **Raw TypeScript**
|
|
|
57
57
|
- `WeatherPageHeaderForecast`
|
|
58
58
|
- `WeatherPageHeaderAlert`
|
|
59
59
|
- `WeatherPageHeaderNextDays`
|
|
60
|
+
- `useWeather`
|
|
61
|
+
- `useWeatherAlert`
|
|
60
62
|
- `weatherCoordinate`
|
|
61
|
-
- `
|
|
62
|
-
- `useOpenWeatherMap`
|
|
63
|
-
- `resolveWeatherProvider`
|
|
64
|
-
- `WEATHER_PROVIDER_CAPABILITIES`
|
|
63
|
+
- `weatherFarmIdx`
|
|
65
64
|
- `WeatherPageHeaderContainerProps`
|
|
66
65
|
- `WeatherPageHeaderTexts`
|
|
67
|
-
- `WeatherProviderKey`
|
|
68
|
-
- `WeatherProviderCapability`
|
|
69
66
|
- `/service-inquiry`
|
|
70
67
|
- `ServiceInquiry.Form`
|
|
71
68
|
- `ServiceInquiry.OpenButton`
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { API_Res_WeatherKoreaBase } from "../types";
|
|
2
|
+
|
|
3
|
+
const API_RES_RAW = {
|
|
4
|
+
response: {
|
|
5
|
+
header: {
|
|
6
|
+
resultCode: "",
|
|
7
|
+
resultMsg: "",
|
|
8
|
+
},
|
|
9
|
+
body: {
|
|
10
|
+
dataType: "",
|
|
11
|
+
items: {
|
|
12
|
+
item: [],
|
|
13
|
+
},
|
|
14
|
+
pageNo: 0,
|
|
15
|
+
numOfRows: 0,
|
|
16
|
+
totalCount: 0,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const API_RES_BASE: API_Res_WeatherKoreaBase = {
|
|
22
|
+
sky: null,
|
|
23
|
+
drop: null,
|
|
24
|
+
rainAmount: null,
|
|
25
|
+
snowAmount: null,
|
|
26
|
+
windSpeed: null,
|
|
27
|
+
condition: null,
|
|
28
|
+
conditionName: null,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const WEATHER_KOREA_RESPONSE = {
|
|
32
|
+
API_RES_RAW,
|
|
33
|
+
API_RES_BASE,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default WEATHER_KOREA_RESPONSE;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
|
-
import { weatherCoordinate } from "../jotai";
|
|
5
4
|
import {
|
|
6
5
|
useQueryWeatherOpenWeatherMapForecast,
|
|
7
6
|
useQueryWeatherOpenWeatherMapNow,
|
|
8
7
|
} from "../apis/open-weather-map/client";
|
|
8
|
+
import { weatherCoordinate } from "../../jotai";
|
|
9
9
|
import type {
|
|
10
10
|
WeatherApiLocaleOptions,
|
|
11
11
|
WeatherOpenWeatherMapParams,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
4
|
import { useAtomValue } from "jotai";
|
|
5
5
|
import {
|
|
6
6
|
useQueryWeatherKoreaForecast,
|
|
7
7
|
useQueryWeatherKoreaNow,
|
|
8
|
-
} from "../apis/korea/client";
|
|
8
|
+
} from "../apis/korea/client";
|
|
9
|
+
import { weatherCoordinate } from "../../jotai/coordinate";
|
|
9
10
|
import type { API_Req_WeatherKorea } from "../types";
|
|
10
|
-
import { weatherCoordinate } from "../jotai/coordinate";
|
|
11
11
|
import { getWeatherBaseMoments } from "../utils/date-time";
|
|
12
12
|
import { getWeatherGridLocation } from "../utils/location";
|
|
13
13
|
|
|
@@ -35,10 +35,7 @@ export default function useWeatherKorea({
|
|
|
35
35
|
coordinate.lat !== null ||
|
|
36
36
|
coordinate.lng !== null;
|
|
37
37
|
|
|
38
|
-
if (!hasInjectedCoordinate)
|
|
39
|
-
// 변경 설명: 초기 mount에서 coordinate 주입 전에는 geolocation/web API 준비를 시도하지 않는다.
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
38
|
+
if (!hasInjectedCoordinate) return;
|
|
42
39
|
|
|
43
40
|
const nowMoments = getWeatherBaseMoments("now");
|
|
44
41
|
const forecastMoments = getWeatherBaseMoments("forecast");
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { useAtomValue } from "jotai";
|
|
5
|
-
import { useQueryWeatherKoreaAlert } from "../apis/korea/client";
|
|
6
|
-
import { weatherFarmIdx } from "
|
|
5
|
+
import { useQueryWeatherKoreaAlert } from "../apis/korea/client";
|
|
6
|
+
import { weatherFarmIdx } from "../../jotai/farm-idx";
|
|
7
7
|
|
|
8
8
|
type UseWeatherKoreaAlertOptions = {
|
|
9
9
|
enabled?: boolean;
|
|
@@ -13,8 +13,6 @@ export default function useWeatherKoreaAlert({
|
|
|
13
13
|
enabled = true,
|
|
14
14
|
}: UseWeatherKoreaAlertOptions = {}) {
|
|
15
15
|
const farm_idx = useAtomValue(weatherFarmIdx);
|
|
16
|
-
|
|
17
|
-
// 특보 조회 식별자는 템플릿이 보정하지 않고, 서비스가 주입한 값만 그대로 사용한다.
|
|
18
16
|
const { data, isFetching } = useQueryWeatherKoreaAlert(
|
|
19
17
|
{
|
|
20
18
|
farm_idx,
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
KMA_Res_WeatherNow,
|
|
3
|
+
KMA_Res_WeatherForecast,
|
|
4
|
+
KMA_Res_Base,
|
|
5
|
+
KMA_Req_BaseGridCoordinate,
|
|
6
|
+
KMA_Req_BaseMoments,
|
|
7
|
+
KMA_Res_AlertType,
|
|
8
|
+
KMA_Res_AlertLevel,
|
|
9
|
+
KMA_Res_AlertCommand,
|
|
10
|
+
KMA_Res_WeatherItem,
|
|
11
|
+
} from "./korea";
|
|
12
|
+
|
|
13
|
+
export type API_Req_WeatherKoreaGrid = KMA_Req_BaseGridCoordinate;
|
|
14
|
+
export type API_Req_WeatherKoreaMoments = KMA_Req_BaseMoments;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 기상청 API; 날씨 요청.
|
|
18
|
+
*/
|
|
19
|
+
export type API_Req_WeatherKorea = API_Req_WeatherKoreaGrid &
|
|
20
|
+
Partial<API_Req_WeatherKoreaMoments>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 기상청 API; 특보 요청 파라미터
|
|
24
|
+
* @property {number | null} farm_idx 농장 식별자
|
|
25
|
+
*/
|
|
26
|
+
export type API_Req_WeatherKoreaAlert = { farm_idx: number | null };
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 기상청 API; 특보 응답 타입 - 지역별 특보정보
|
|
30
|
+
*/
|
|
31
|
+
export type API_Res_WeatherKoreaAlertEach = {
|
|
32
|
+
/**
|
|
33
|
+
* 상위 지역 코드
|
|
34
|
+
* @see https://apihub.kma.go.kr/api/typ01/url/wrn_reg.php?tmfc=0&authKey=njld-D40Rb25Xfg-NAW9hA
|
|
35
|
+
*/
|
|
36
|
+
upper_region_code: string;
|
|
37
|
+
/**
|
|
38
|
+
* 상위 지역 이름
|
|
39
|
+
* @see https://apihub.kma.go.kr/api/typ01/url/wrn_reg.php?tmfc=0&authKey=njld-D40Rb25Xfg-NAW9hA
|
|
40
|
+
*/
|
|
41
|
+
upper_region_name: string;
|
|
42
|
+
/**
|
|
43
|
+
* 특보 지역 코드
|
|
44
|
+
* @see https://apihub.kma.go.kr/api/typ01/url/wrn_reg.php?tmfc=0&authKey=njld-D40Rb25Xfg-NAW9hA
|
|
45
|
+
*/
|
|
46
|
+
alert_region_code: string;
|
|
47
|
+
/**
|
|
48
|
+
* 특보 지역 이름
|
|
49
|
+
* @see https://apihub.kma.go.kr/api/typ01/url/wrn_reg.php?tmfc=0&authKey=njld-D40Rb25Xfg-NAW9hA
|
|
50
|
+
*/
|
|
51
|
+
alert_region_name: string;
|
|
52
|
+
/**
|
|
53
|
+
* 특보 발표시간
|
|
54
|
+
* - yyyymmddhhmm
|
|
55
|
+
*/
|
|
56
|
+
announcement_time: string;
|
|
57
|
+
/**
|
|
58
|
+
* 특보 발효시간
|
|
59
|
+
* - yyyymmddhhmm
|
|
60
|
+
*/
|
|
61
|
+
effective_time: string;
|
|
62
|
+
/** * 특보 종류 (한글) */
|
|
63
|
+
alert_type: KMA_Res_AlertType;
|
|
64
|
+
/**
|
|
65
|
+
* 특보 수준 (한글)
|
|
66
|
+
* - 경보, 주의, 예비
|
|
67
|
+
*/
|
|
68
|
+
alert_level: KMA_Res_AlertLevel;
|
|
69
|
+
/**
|
|
70
|
+
* 특보 명령 (한글)
|
|
71
|
+
* - 발표, 변경, 해제
|
|
72
|
+
*/
|
|
73
|
+
alert_command: KMA_Res_AlertCommand;
|
|
74
|
+
/**
|
|
75
|
+
* 특보 해제 예고시점 (한글)
|
|
76
|
+
* - dd일 오전/오후/밤(HH시 ~ HH시)
|
|
77
|
+
*/
|
|
78
|
+
cancel_notice_time: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* 기상청 API; 특보 응답 타입
|
|
82
|
+
*/
|
|
83
|
+
export type API_Res_WeatherKoreaAlert = {
|
|
84
|
+
/** * 특보 발표 시간 */
|
|
85
|
+
api_announcement_time: string | null;
|
|
86
|
+
/** * 총 특보 개수 */
|
|
87
|
+
total_count: number;
|
|
88
|
+
/** * 지역별 특보 */
|
|
89
|
+
alerts: API_Res_WeatherKoreaAlertEach[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 기상청 API; 기초 정보
|
|
94
|
+
* @property {string} condition - 개황 정보
|
|
95
|
+
*/
|
|
96
|
+
export type API_Res_WeatherKoreaBase = {
|
|
97
|
+
/**
|
|
98
|
+
* 하늘상태 코드
|
|
99
|
+
* - 1(맑음)
|
|
100
|
+
* - 2(구름조금)
|
|
101
|
+
* - 3(구름많음)
|
|
102
|
+
* - 4(흐림)
|
|
103
|
+
*/
|
|
104
|
+
sky: string | null;
|
|
105
|
+
/**
|
|
106
|
+
* 강수형태 코드
|
|
107
|
+
* - 0(없음)
|
|
108
|
+
* - 1(비)
|
|
109
|
+
* - 2(비/눈)
|
|
110
|
+
* - 3(눈)
|
|
111
|
+
* - 4(소나기)
|
|
112
|
+
*/
|
|
113
|
+
drop: string | null;
|
|
114
|
+
/**
|
|
115
|
+
* 강수량 코드
|
|
116
|
+
* - 1(약한 비); 3mm/h 미만
|
|
117
|
+
* - 2(보통 비); 3mm/h 이상 15mm/h 미만
|
|
118
|
+
* - 3(강한 비); 15mm/h 이상
|
|
119
|
+
*/
|
|
120
|
+
rainAmount: string | null;
|
|
121
|
+
/**
|
|
122
|
+
* 강설량 코드
|
|
123
|
+
* - 1(보통 눈); 1cm/h 미만
|
|
124
|
+
* - 2(많은 눈); 1cm/h 이상
|
|
125
|
+
*/
|
|
126
|
+
snowAmount: string | null;
|
|
127
|
+
/**
|
|
128
|
+
* 풍속 코드
|
|
129
|
+
* - 1(약한 바람); 4m/s 미만
|
|
130
|
+
* - 2(약간 강한 바람); 4m/s 이상 9m/s 미만
|
|
131
|
+
* - 3(강한 바람); 9m/s 이상
|
|
132
|
+
*/
|
|
133
|
+
windSpeed: string | null;
|
|
134
|
+
/**
|
|
135
|
+
* 날씨 상태 코드
|
|
136
|
+
* - sky-1(맑음)
|
|
137
|
+
* - sky-2(구름조금)
|
|
138
|
+
* - sky-3(구름많음)
|
|
139
|
+
* - sky-4(흐림)
|
|
140
|
+
* - drop-rain-shower(소나기)
|
|
141
|
+
* - drop-rain(비)
|
|
142
|
+
* - drop-rain-1(약한 비)
|
|
143
|
+
* - drop-rain-2(보통 비)
|
|
144
|
+
* - drop-rain-3(강한 비)
|
|
145
|
+
* - drop-rain-snow(비/눈)
|
|
146
|
+
* - drop-rain-snow-1(약한 비/눈)
|
|
147
|
+
* - drop-rain-snow-2(보통 비/눈)
|
|
148
|
+
* - drop-rain-snow-3(강한 비/눈)
|
|
149
|
+
* - drop-snow(눈)
|
|
150
|
+
* - drop-snow-1(보통 눈)
|
|
151
|
+
* - drop-snow-2(많은 눈)
|
|
152
|
+
*/
|
|
153
|
+
condition: string | null;
|
|
154
|
+
/** 날씨 상태 텍스트 */
|
|
155
|
+
conditionName: string | null;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 기상청 API; 오늘 날씨
|
|
160
|
+
* @property {number|string|null} temperature 기온(℃)
|
|
161
|
+
* @property {number|string|null} max_temperature 최고기온(℃)
|
|
162
|
+
* @property {number|string|null} min_temperature 최저기온(℃)
|
|
163
|
+
* @property {number|string|null} humidity 습도(%)
|
|
164
|
+
*/
|
|
165
|
+
export type API_Res_WeatherKoreaToday = API_Res_WeatherKoreaBase & {
|
|
166
|
+
/** 기온(℃) */
|
|
167
|
+
temperature: number | string | null;
|
|
168
|
+
/** 최고 기온(℃) */
|
|
169
|
+
max_temperature: number | string | null;
|
|
170
|
+
/** 최저 기온(℃) */
|
|
171
|
+
min_temperature: number | string | null;
|
|
172
|
+
/** 습도(%) */
|
|
173
|
+
humidity: number | string | null;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 기상청 API; 내일/모레 날씨
|
|
178
|
+
* @property {number|string|null} max_temperature 최고기온(℃)
|
|
179
|
+
* @property {number|string|null} min_temperature 최저기온(℃)
|
|
180
|
+
*/
|
|
181
|
+
export type API_Res_WeatherKoreaNextDays = API_Res_WeatherKoreaBase & {
|
|
182
|
+
/** 최고 기온(℃) */
|
|
183
|
+
max_temperature: number | string | null;
|
|
184
|
+
/** 최저 기온(℃) */
|
|
185
|
+
min_temperature: number | string | null;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 기상청 API; 원본 응답
|
|
190
|
+
* @property {RAW} raw API 원본 데이터
|
|
191
|
+
*/
|
|
192
|
+
export type API_Res_WeatherKoreaRaw<RAW = KMA_Res_Base<KMA_Res_WeatherItem>> = {
|
|
193
|
+
/** API 원본 */
|
|
194
|
+
raw: RAW;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 기상청 API; 초단기 실황 구조
|
|
199
|
+
* @property {API_Res_WeatherKoreaToday} today 오늘 데이터
|
|
200
|
+
*/
|
|
201
|
+
export type API_Res_WeatherKoreaNow =
|
|
202
|
+
API_Res_WeatherKoreaRaw<KMA_Res_WeatherNow> & {
|
|
203
|
+
/** 오늘 날씨 */
|
|
204
|
+
today: API_Res_WeatherKoreaToday;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 기상청 API; 단기 예보 구조
|
|
209
|
+
* @property {API_Res_WeatherKoreaToday} today 오늘 데이터
|
|
210
|
+
* @property {API_Res_WeatherKoreaNextDays} day_1 내일 데이터
|
|
211
|
+
* @property {API_Res_WeatherKoreaNextDays} day_2 모레 데이터
|
|
212
|
+
*/
|
|
213
|
+
export type API_Res_WeatherKoreaForecast =
|
|
214
|
+
API_Res_WeatherKoreaRaw<KMA_Res_WeatherForecast> & {
|
|
215
|
+
/** 오늘 날씨 */
|
|
216
|
+
today: API_Res_WeatherKoreaToday;
|
|
217
|
+
/** 내일 날씨 */
|
|
218
|
+
day_1: API_Res_WeatherKoreaNextDays;
|
|
219
|
+
/** 모레 날씨 */
|
|
220
|
+
day_2: API_Res_WeatherKoreaNextDays;
|
|
221
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 위경도 좌표.
|
|
3
|
+
* @property {number | null} latitude 위도
|
|
4
|
+
* @property {number | null} longitude 경도
|
|
5
|
+
*/
|
|
6
|
+
export type GeoCoordinate = {
|
|
7
|
+
/** 위도 */
|
|
8
|
+
latitude: number | null;
|
|
9
|
+
/** 경도 */
|
|
10
|
+
longitude: number | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 날씨 API에서 사용하는 위경도 좌표.
|
|
15
|
+
* @property {number | null} lat 위도
|
|
16
|
+
* @property {number | null} lng 경도
|
|
17
|
+
*/
|
|
18
|
+
export type WeatherGeoCoordinate = {
|
|
19
|
+
/** 위도 */
|
|
20
|
+
lat: number | null;
|
|
21
|
+
/** 경도 */
|
|
22
|
+
lng: number | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 날씨 API locale 요청 옵션.
|
|
27
|
+
* @property {string} [locale] 요청 언어 코드. 기본 지원값은 ko, en, ja이며 타입은 확장 가능한 string으로 유지한다.
|
|
28
|
+
*/
|
|
29
|
+
export interface WeatherApiLocaleOptions {
|
|
30
|
+
/**
|
|
31
|
+
* 요청 언어 코드. 기본 지원값은 ko, en, ja이며 타입은 확장 가능한 string으로 유지한다.
|
|
32
|
+
*/
|
|
33
|
+
locale?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* OpenWeatherMap 요청 파라미터.
|
|
38
|
+
* @property {number | null} lat 위도
|
|
39
|
+
* @property {number | null} lng 경도
|
|
40
|
+
* @property {string} [locale] 요청 언어 코드
|
|
41
|
+
*/
|
|
42
|
+
export type WeatherOpenWeatherMapParams = WeatherGeoCoordinate &
|
|
43
|
+
WeatherApiLocaleOptions;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 좌표와 주소 정보를 함께 담는 구조.
|
|
47
|
+
* @property {string | null} address 좌표 주소
|
|
48
|
+
*/
|
|
49
|
+
export type WeatherCoordinate = {
|
|
50
|
+
/** 좌표 주소 */
|
|
51
|
+
address: string | null;
|
|
52
|
+
} & WeatherGeoCoordinate;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 날씨 로직에서 사용하는 날짜 기준값 모음.
|
|
56
|
+
* @property {Date} now 현재 시각
|
|
57
|
+
* @property {string} today 오늘 날짜(yyyymmdd)
|
|
58
|
+
* @property {string} tomorrow 내일 날짜(yyyymmdd)
|
|
59
|
+
* @property {string} dayAfterTomorrow 모레 날짜(yyyymmdd)
|
|
60
|
+
*/
|
|
61
|
+
export type WeatherUtilDateTimeMoments = {
|
|
62
|
+
/** 현재 시각 */
|
|
63
|
+
now: Date;
|
|
64
|
+
/** 오늘 날짜(yyyymmdd) */
|
|
65
|
+
today: string;
|
|
66
|
+
/** 내일 날짜(yyyymmdd) */
|
|
67
|
+
tomorrow: string;
|
|
68
|
+
/** 모레 날짜(yyyymmdd) */
|
|
69
|
+
dayAfterTomorrow: string;
|
|
70
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const OPEN_WEATHER_MAP_LANG_BY_LOCALE: Record<string, string> = {
|
|
2
|
+
ko: "kr",
|
|
3
|
+
en: "en",
|
|
4
|
+
ja: "ja",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Weather Locale; locale 기준 키 정규화
|
|
9
|
+
* @param {string} [locale] locale 코드
|
|
10
|
+
* @return {string} 전달된 locale의 기본 언어 코드
|
|
11
|
+
* @example
|
|
12
|
+
* getWeatherLocaleKey("ko-KR")
|
|
13
|
+
*/
|
|
14
|
+
export const getWeatherLocaleKey = (locale?: string): string =>
|
|
15
|
+
locale?.trim().toLowerCase().split(/[-_]/)[0] || "ko";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Weather Locale; OpenWeatherMap API lang 값 변환
|
|
19
|
+
* @param {string} [locale] locale 코드
|
|
20
|
+
* @return {string} OpenWeatherMap lang 파라미터
|
|
21
|
+
* @example
|
|
22
|
+
* getOpenWeatherMapLang("ko")
|
|
23
|
+
*/
|
|
24
|
+
export const getOpenWeatherMapLang = (locale?: string): string => {
|
|
25
|
+
const localeKey = getWeatherLocaleKey(locale);
|
|
26
|
+
|
|
27
|
+
return OPEN_WEATHER_MAP_LANG_BY_LOCALE[localeKey] ?? localeKey;
|
|
28
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GeoCoordinate,
|
|
3
|
+
KMA_GridCoordinate,
|
|
4
|
+
KMA_Req_BaseGridCoordinate,
|
|
5
|
+
WeatherCoordinate,
|
|
6
|
+
} from "../types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 날씨 도구; 접속위치 추출
|
|
10
|
+
* @util
|
|
11
|
+
* @return {GeoCoordinate}}
|
|
12
|
+
*/
|
|
13
|
+
export const userLocation = async (): Promise<GeoCoordinate> => {
|
|
14
|
+
if (typeof window === "undefined" || !window.navigator.geolocation) {
|
|
15
|
+
return { latitude: null, longitude: null };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const position = await new Promise<GeolocationPosition>(
|
|
20
|
+
(resolve, reject) => {
|
|
21
|
+
window.navigator.geolocation.getCurrentPosition(resolve, reject);
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
return {
|
|
25
|
+
latitude: position.coords.latitude,
|
|
26
|
+
longitude: position.coords.longitude,
|
|
27
|
+
};
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error("[userLocation] 위치 정보 에러", error);
|
|
30
|
+
return { latitude: null, longitude: null };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 날씨 도구; 위경도 -> 격자 좌표 변환
|
|
36
|
+
* @util
|
|
37
|
+
* @param {GeoCoordinate} coordinate 위경도 좌표
|
|
38
|
+
* @param {number} coordinate.latitude 위도
|
|
39
|
+
* @param {number} coordinate.longitude 경도
|
|
40
|
+
* @return {KMA_GridCoordinate} 격자 좌표
|
|
41
|
+
* @see https://apihub.kma.go.kr/
|
|
42
|
+
*/
|
|
43
|
+
export function convertCoordinateToGrid({
|
|
44
|
+
latitude,
|
|
45
|
+
longitude,
|
|
46
|
+
}: GeoCoordinate): KMA_GridCoordinate {
|
|
47
|
+
const res: KMA_GridCoordinate = { x: 0, y: 0 };
|
|
48
|
+
|
|
49
|
+
if (latitude === null || longitude === null) return res;
|
|
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 origin_longitude = ORIGIN_LONGITUDE * RADIAN;
|
|
65
|
+
const origin_latitude = 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
|
+
|
|
77
|
+
const ro =
|
|
78
|
+
(re * sfPow) /
|
|
79
|
+
Math.pow(Math.tan(Math.PI * 0.25 + origin_latitude * 0.5), sn);
|
|
80
|
+
|
|
81
|
+
const ra =
|
|
82
|
+
(re * sfPow) /
|
|
83
|
+
Math.pow(Math.tan(Math.PI * 0.25 + latitude * RADIAN * 0.5), sn);
|
|
84
|
+
let theta = longitude * RADIAN - origin_longitude;
|
|
85
|
+
if (theta > Math.PI) theta -= 2.0 * Math.PI;
|
|
86
|
+
if (theta < -Math.PI) theta += 2.0 * Math.PI;
|
|
87
|
+
theta *= sn;
|
|
88
|
+
|
|
89
|
+
res.x = Math.floor(ra * Math.sin(theta) + XO + 0.5);
|
|
90
|
+
res.y = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
|
|
91
|
+
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 날씨 도구; 위도/경도 -> 기상청 격자 좌표 변환
|
|
97
|
+
* @util
|
|
98
|
+
* @param {WeatherCoordinate} [coordinate] 사용자 위치 이외의 특정 위도/경도 좌표
|
|
99
|
+
* @param {number} coordinate.lat 위도
|
|
100
|
+
* @param {number} coordinate.lng 경도
|
|
101
|
+
* @return {KMA_Req_BaseGridCoordinate} 기상청 격자 좌표
|
|
102
|
+
*/
|
|
103
|
+
export async function getWeatherGridLocation(
|
|
104
|
+
coordinate?: WeatherCoordinate,
|
|
105
|
+
): Promise<KMA_Req_BaseGridCoordinate> {
|
|
106
|
+
const res: KMA_Req_BaseGridCoordinate = { nx: 0, ny: 0 };
|
|
107
|
+
|
|
108
|
+
if (typeof window === "undefined") return res;
|
|
109
|
+
|
|
110
|
+
const geo: GeoCoordinate = {
|
|
111
|
+
latitude: coordinate?.lat ?? null,
|
|
112
|
+
longitude: coordinate?.lng ?? null,
|
|
113
|
+
};
|
|
114
|
+
const hasInjectedAddress =
|
|
115
|
+
typeof coordinate?.address === "string" && coordinate.address.trim() !== "";
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
typeof window !== "undefined" &&
|
|
119
|
+
!hasInjectedAddress &&
|
|
120
|
+
(typeof coordinate === "undefined" ||
|
|
121
|
+
geo.latitude === null ||
|
|
122
|
+
geo.longitude === null)
|
|
123
|
+
) {
|
|
124
|
+
const { latitude, longitude } = await userLocation();
|
|
125
|
+
|
|
126
|
+
if (latitude === null || longitude === null) return res;
|
|
127
|
+
geo.latitude = latitude;
|
|
128
|
+
geo.longitude = longitude;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const grid = convertCoordinateToGrid(geo);
|
|
132
|
+
|
|
133
|
+
if (grid.x === 0 || grid.y === 0) return res;
|
|
134
|
+
|
|
135
|
+
res.nx = grid.x;
|
|
136
|
+
res.ny = grid.y;
|
|
137
|
+
|
|
138
|
+
return res;
|
|
139
|
+
}
|