๐ค๏ธ ๋์งํธ ์ฌ์ด๋์ง ๋ ์จ ์์ ฏ ๊ฐ๋ฐ๊ธฐ: ๋ฌด๋ฃ API์ ํจ์ ๋ถํฐ ๊ธฐ์์ฒญ API ํ๋ธ ์ ์ฐฉ๊น์ง
๋์งํธ ์ฌ์ด๋์ง ์๋ฃจ์ ์ ํด๋ผ์ด์ธํธ์ ํ๋ก ํธ์๋๋ฅผ ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด, ๊ณ ๊ฐ์ฌ ํ๋ฉด ํ๊ตฌ์์ ์ฐจ์งํ๋ '๋ ์จ ์์ ฏ'์ ๊ตฌํํด์ผ ํ ๋๊ฐ ๋ฐ๋์ ์จ๋ค.
๋จ์ํ ๊ธฐ์จ๊ณผ ๋ง์/ํ๋ฆผ ์์ด์ฝ๋ง ๋์ฐ๋ฉด ๋๋ ๊ฐ๋จํ ์์ ์ฒ๋ผ ๋ณด์ด์ง๋ง, ๋ง์ ์ค๋ฌด์ ์ ์ฉํ๋ ค๊ณ ํ๋ฉด ๋ผ์ด์ ์ค, API ํธ์ถ ์ ํ, ๋ฐ์ดํฐ ์ ํ๋ ๋ฑ ๊ณ ๋ คํด์ผ ํ ์ฐ์ด ํ๋ ๊ฐ๊ฐ ์๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ํด์ธ ๋ฌด๋ฃ API์์ ์์ํด ๊ธฐ์์ฒญ API ํ๋ธ์ ์ ์ฐฉํ๊ธฐ๊น์ง์ ์ข์ถฉ์ฐ๋ ํธ๋ฌ๋ธ์ํ ๊ณผ์ ๊ณผ ํต์ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ ๋ฆฌํด ๋ณธ๋ค.
1. ์ฒซ ๋ฒ์งธ ์๋: Open-Meteo์ ๋ก์ปฌ์คํ ๋ฆฌ์ง ์บ์ฑ์ ์กฐํฉ
์ด๊ธฐ ํ๋กํ ํ์ ์ Open-Meteo API๋ฅผ ํ์ฉํด ๊ตฌํํ๋ค. ๋ณ๋์ ํค ๋ฐ๊ธ ์์ด ์๊ฒฝ๋๋ง ๋์ง๋ฉด ์ฆ์ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํด ์ฃผ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ์์๋ ์ด๋งํ ๋ ์์ด ์์๋ค.
์ฌ์ด๋์ง ํน์ฑ์ ๋์คํ๋ ์ด๊ฐ 24์๊ฐ ์ผ์ ธ ์์ผ๋ฏ๋ก, API ์ผ์ผ ํธ์ถ ์ ํ(Free Tier)์ ๋ฐฉ์ดํ๋ ๊ฒ์ด ํต์ฌ์ด์๋ค.
- ํด๊ฒฐ์ฑ
:
localStorage๋ฅผ ํ์ฉํด ํ ๋ฒ ๋ฐ์์จ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ 55๋ถ๊ฐ ์บ์ฑํ๋๋ก ์ค๊ณํ๋ค. (์๋ ๊ฐฑ์ ์ธํฐ๋ฒ์ 60๋ถ์ผ๋ก ์ค์ ํ์ฌ, ์บ์ ๋ง๋ฃ ์งํ ์์ฐ์ค๋ฝ๊ฒ ์ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๋๋ก ํ์ด๋จธ๋ฅผ ๋ง์ท๋ค.)
์ฑ๋ฅ๋ ์๋ต ์๋๋ ์๋ฒฝํ๋ค. ํ์ง๋ง ์ง์ง ๋ฌธ์ ๋ ์ฝ๋ ๋ฐ, **'๋ผ์ด์ ์ค'**์ ์์๋ค.
2. ์์ ์ ์ด์ฉ์ ๋ฒฝ: ๋ฌด๋ฃ API๋ ์ง์ง ๋ฌด๋ฃ๊ฐ ์๋๋ค
ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉํํ๋ ์์ ์ฉ(B2B) ์๋น์ค์ Open-Meteo์ ๋ฌด๋ฃ ์๋ํฌ์ธํธ๋ฅผ ๊ทธ๋๋ก ์ฐ๋ ๊ฒ์ด ๋ฒ์ ์ผ๋ก ๋ฌธ์ ๊ฐ ์๋์ง ๊ฒํ ํด ๋ณด์๋ค. ๊ฒฐ๋ก ์ **'์ ๋ ๋ถ๊ฐ'**์๋ค.
๋๋ถ๋ถ์ ํด์ธ ๋ฌด๋ฃ ๋ ์จ API๋ ๊ฐ์ธ ํ๋ก์ ํธ๋ ๋น์์ ์ ์ฉ๋์ ํํด์๋ง ๋ฌด๋ฃ ํธ๋ํฝ์ ์ด์ด๋๋ค. ์ถ์ฒ(CC BY 4.0)๋ฅผ ํ๋ฉด์ ๋ช ์ํ๋๋ผ๋, ์์ ์ฉ ์๋น์ค์์ ๋ฌด๋ฃ ์๋ฒ๋ก ๊ณ์ ์์ฒญ์ ๋ณด๋ด๋ฉด ์ฝ๊ด ์๋ฐ์ผ๋ก ์ด๋ ๋ ๊ฐ์๊ธฐ IP๊ฐ ์๊ตฌ ์ฐจ๋จ๋ ๋ฆฌ์คํฌ๊ฐ ์ปธ๋ค. ๊ฒฐ๊ตญ ๋ผ์ด์ ์ค ์ด์์์ 100% ์์ ๋กญ๊ณ ์์ ์ ์ด์ฉ์ด ํ์ฉ๋๋ ๊ธฐ์์ฒญ ๊ณต๊ณต๋ฐ์ดํฐ๋ก ๋ฐฉํฅ์ ํ์ด์ผ ํ๋ค.
3. ๊ธฐ์์ฒญ API ๋์ ๊ธฐ: "returnReasonCode: 12"์ ๋ช
์์ ํ ๋ํ๋ฏผ๊ตญ ๊ธฐ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ธฐ๋ก ํ๊ณ API ์ฐ๋์ ์๋ํ๋ค. ์๊ฒฝ๋๋ฅผ ๊ธฐ์์ฒญ ์ ์ฉ '๊ฒฉ์ ์ขํ(nx, ny)'๋ก ๋ณํํ๋ ๊ณต์๊น์ง ์ ์ฉํ์ฌ ์๋ฒฝํ๊ฒ ์ค๋นํ๋ค๊ณ ์๊ฐํ์ง๋ง, ๋์์จ ๊ฒ์ ์ฐจ๊ฐ์ด ์๋ฌ ๋ฉ์์ง์๋ค.
returnReasonCode: "12" (ํด๋น ์คํAPI ์๋น์ค๊ฐ ์๊ฑฐ๋ ํ๊ธฐ๋จ)์์ธ์ ๋ถ์ํ๋ ๊ณผ์ ์์ **๊ณต๊ณต๋ฐ์ดํฐํฌํธ(data.go.kr)**๊ณผ **๊ธฐ์์ฒญ API ํ๋ธ(apihub.kma.go.kr)**๋ฅผ ํผ๋ํ๊ณ ์์๋ค๋ ์ฌ์ค์ ๋ฐ๊ฒฌํ๋ค. ๊ธฐ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๋ค๋ ์ ์ ๊ฐ์ง๋ง, ๋ ๊ณณ์ ๋ฐ๊ธํ๋ ํค์ ์ข ๋ฅ(serviceKey vs authKey)์ ์๋ํฌ์ธํธ ์ฃผ์๊ฐ ์์ ํ ๋ฌ๋๋ ๊ฒ์ด๋ค.
4. ์น๋ช ์ ์ธ ๋ฐ๊ฒฌ: '์คํฉ' ๋ฐ์ดํฐ์๋ ํ๋์ด ์๋ค?
๋ฌธ์๋ฅผ ํ๊ณ ๋ค๋ฉฐ ๊ฐ์ฅ ํฌ๊ฒ ๊นจ๋ฌ์ ์ํคํ ์ฒ์ ์ด์๊ฐ ํ๋ ๋ ์์๋ค. ์ฒ์์ ๊ตฌํํ๋ ค ํ๋ ๊ธฐ์์ฒญ์ '์ด๋จ๊ธฐ์คํฉ(getUltraSrtNcst)' API์๋ ์์ฃผ ์น๋ช ์ ์ธ ๋จ์ ์ด ์กด์ฌํ๋ค.
- ์คํฉ ๋ฐ์ดํฐ์๋ ๊ธฐ์จ, ์ต๋, ํ์, ๊ฐ์ ํํ(๋น/๋)๋ ์์ง๋ง, ๊ฐ์ฅ ์ค์ํ 'ํ๋ ์ํ(SKY - ๋ง์/๊ตฌ๋ฆ๋ง์/ํ๋ฆผ)' ๋ฐ์ดํฐ๊ฐ ์์ ์กด์ฌํ์ง ์์๋ค.
๋น๊ฐ ์ค์ง ์์ ๋ ์ฌ์ด๋์ง ํ๋ฉด์ ํด(โ๏ธ)๋ฅผ ๋์์ผ ํ ์ง, ๊ตฌ๋ฆ(โ๏ธ)์ ๋์์ผ ํ ์ง ์ ๊ธธ์ด ์๋ ๊ฒ์ด๋ค. AWS(์๋๊ธฐ์๊ด์ธก์ฅ๋น)๋ ๊ธฐ๊ณ์ ์ผ๋ก ์์น๋ง ์ด ๋ฟ, ํ๋์ ๊ตฌ๋ฆ์ด ์ผ๋ง๋ ๊ผ๋์ง ๊ด์ธกํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
5. ์ต์ข ํด๊ฒฐ์ฑ : '์ด๋จ๊ธฐ์๋ณด'๋ก์ ์ ํ์ API ํ๋ธ ์ ์ฐฉ
๊ฒฐ๊ตญ ํ๋ ์ํ(SKY)์ ๊ฐ์ ํํ(PTY)๋ฅผ ์กฐํฉํ์ฌ ๋ ์จ ์์ด์ฝ์ ์ ํํ๊ฒ ๋ ๋๋งํ๊ธฐ ์ํด, ์คํฉ์ด ์๋ '์ด๋จ๊ธฐ์๋ณด(getUltraSrtFcst)' API๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
์ต์ข ์ ์ผ๋ก ๊ตฌ์ถํ ํ์ดํ๋ผ์ธ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ํ๋ซํผ: ๊ณต์ ๋ฌธ์๊ฐ ์ ๊ฐ์ถฐ์ง **๊ธฐ์์ฒญ API ํ๋ธ(apihub.kma.go.kr)**์ ๋จ๊ธฐ์๋ณด ์กฐํ์๋น์ค(2.0)๋ฅผ ์ฌ์ฉ.
- ๋ฐ์ดํฐ: ์ด๋จ๊ธฐ์๋ณด์กฐํ(getUltraSrtFcst)๋ฅผ ํธ์ถํ์ฌ ๋ง์/ํ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ํ๋ณด.
- ํธ์ถ ์ต์ ํ: ์ด๋จ๊ธฐ์๋ณด๊ฐ ์ ๋ฐ์ดํธ๋๋ '๋งค์ 45๋ถ' ๊ท์น์ ๋ง์ถฐ ์์คํ ์๊ฐ์ ๊ณ์ฐํ์ฌ ์์ฒญ.
- ์์ด์ฝ ๋ ๋๋ง ๋ก์ง: ๊ฐ์๊ฐ ์์ ๋๋ ๋น/๋ ์์ด์ฝ์ ์ฐ์ ํ๊ณ , ๊ฐ์๊ฐ ์์ ๋๋ง ํ๋ ์ํ(SKY) ์ฝ๋๋ฅผ ์ฐธ์กฐํ์ฌ ํด๋ ๊ตฌ๋ฆ์ ๋์ฐ๋ ๋ถ๊ธฐ ๋ก์ง ์ ์ฉ.
6. ๋ ์จ ์์ ฏ ํต์ฌ ์๊ณ ๋ฆฌ์ฆ (Pseudo-code)
๊ตฌ์ฒด์ ์ธ ์ธ์ด์ ์ข ์๋์ง ์๋๋ก, ์ ์ฒด์ ์ธ ๋ ผ๋ฆฌ ํ๋ฆ๊ณผ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ์๋ ์ฝ๋(Pseudo-code)๋ก ์ ๋ฆฌํด ๋์๋ค. ํ๋ก ํธ์๋๋ฟ๋ง ์๋๋ผ ๋ฐฑ์๋๋ก ๋ก์ง์ ์ด๊ดํ ๋๋ ์ ์ฉํ๊ฒ ์ฐ์ผ ์ ์๋ค.
6.1. ๋ฉ์ธ ๋ผ์ดํ์ฌ์ดํด (Main Lifecycle)
ALGORITHM WeatherPlayer_Run:
// 1. UI ๋ ๋๋ง ์ค๋น
IF Target_DOM is NOT found:
PRINT "DOM ์ค๋ฅ: ๋ ๋๋ง ์ค๋จ"
EXIT
// 2. ์ด๊ธฐ 1ํ ์ฆ์ ์คํ
CALL Update_Weather_Flow()
// 3. ์ฃผ๊ธฐ์ ์คํ (ํ์ด๋จธ)
START Timer(Interval: 60 minutes) DO:
CALL Update_Weather_Flow()6.2. ๋ ์จ ์ ๋ฐ์ดํธ ํ๋ฆ (Update Flow)
ALGORITHM Update_Weather_Flow:
// 1. ์ขํ ๋ณํ (WGS84 -> ๊ธฐ์์ฒญ ๊ฒฉ์)
(nx, ny) = CALL Convert_LatLon_To_Grid(User_Lat, User_Lon)
// 2. ๋ฐ์ดํฐ ์ทจ๋ (์บ์ OR API)
WeatherData = CALL Fetch_Weather_Data(nx, ny)
// 3. ํ๋ฉด ๋ ๋๋ง
IF WeatherData is valid:
CALL Render_Weather_UI(WeatherData)6.3. ๋ฐ์ดํฐ ์ทจ๋ ๋ฐ ์บ์ฑ (Fetch & Cache)
ALGORITHM Fetch_Weather_Data(nx, ny):
CacheKey = "KMA_API_" + nx + "_" + ny
CurrentTime = Get_Current_Time()
CacheTTL = 55 minutes
// 1. ๋ก์ปฌ ์บ์ ํ์ธ
IF Cache_Exists(CacheKey):
CacheData = Load_Cache(CacheKey)
IF (CurrentTime - CacheData.SavedTime) < CacheTTL:
RETURN CacheData.Data // API ํธ์ถ ์์ด ์บ์ ๋ฐํ
// 2. ์บ์๊ฐ ์๊ฑฐ๋ ๋ง๋ฃ๋์๋ค๋ฉด API ํธ์ถ ์ค๋น
(BaseDate, BaseTime) = CALL Calculate_KMA_Time(CurrentTime)
// 3. ๊ธฐ์์ฒญ API ํ๋ธ ํธ์ถ
Response = HTTP_GET(
URL: "[https://apihub.kma.go.kr/.../getUltraSrtFcst](https://apihub.kma.go.kr/.../getUltraSrtFcst)",
Params: AuthKey, BaseDate, BaseTime, nx, ny, dataType="JSON"
)
// 4. ์๋ต ํ์ฑ ๋ฐ ์ถ์ถ (๊ฐ์ฅ ๊ฐ๊น์ด ์๊ฐ๋์ ์๋ณด๋ง ํฝ์
)
ParsedResult = {}
FOR EACH item IN Response.Items:
// T1H, SKY, PTY ๋ฑ ๋์ผํ ์นดํ
๊ณ ๋ฆฌ๊ฐ ์์ง ์์ผ๋ฉด ์ฒซ ๋ฒ์งธ ๊ฐ(์ต์ ) ์ ์ฅ
IF item.category IS NOT IN ParsedResult:
ParsedResult[item.category] = item.fcstValue
// 5. ์ ๋ฐ์ดํฐ ์บ์ฑ
SAVE_CACHE(CacheKey, Data: ParsedResult, SavedTime: CurrentTime)
RETURN ParsedResult6.4. ๋ฐํ ์๊ฐ ์ฐ์ถ (Time Calculation)
ALGORITHM Calculate_KMA_Time(CurrentTime):
TargetTime = CurrentTime
// ํ์ฌ ์๊ฐ์ด ๋งค์ 45๋ถ ์ด์ ์ด๋ผ๋ฉด, ์์ง ์ต์ ์๋ณด๊ฐ ์๋ฒ์ ์์
IF TargetTime.Minute < 45:
TargetTime = TargetTime - 1 hour // ์ด์ ์๊ฐ๋๋ก ๋กค๋ฐฑ
BaseDate = FORMAT(TargetTime, "YYYYMMDD")
BaseTime = FORMAT(TargetTime, "HH") + "30" // ์ด๋จ๊ธฐ์๋ณด๋ ๋ฌด์กฐ๊ฑด ๋ถ(minute)์ด 30๋ถ
RETURN (BaseDate, BaseTime)6.5. ํ๋ฉด ๋ ๋๋ง ๋ฐ ์์ด์ฝ ํ๋ณ (UI Rendering)
ALGORITHM Render_Weather_UI(Data):
PTY = Data.PTY (๊ฐ์ํํ)
SKY = Data.SKY (ํ๋์ํ)
Temp = Data.T1H (๊ธฐ์จ)
// 1. ๋ ์จ ์์ด์ฝ ํ๋ณ
SkyIcon = ""
RainIcon = ""
IF PTY > 0: // ๋น๋ ๋์ด ์ฌ ๋
SkyIcon = "โ๏ธ" // ๋น๊ฐ ์ค๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋์ ํ๋ฆผ
IF PTY == 1 OR 4 OR 5: RainIcon = "โ" (๋น)
IF PTY == 2 OR 6: RainIcon = "๐ง๏ธโ๏ธ" (๋น/๋)
IF PTY == 3 OR 7: RainIcon = "โ๏ธ" (๋)
ELSE: // ๋น๊ฐ ์ ์ฌ ๋
IF SKY == 1: SkyIcon = "โ๏ธ" (๋ง์)
IF SKY == 3: SkyIcon = "๐ค๏ธ" (๊ตฌ๋ฆ๋ง์)
IF SKY == 4: SkyIcon = "โ๏ธ" (ํ๋ฆผ)
// 2. DOM ์
๋ฐ์ดํธ
UPDATE_DOM(Element: SkyContainer, Value: SkyIcon)
UPDATE_DOM(Element: RainContainer, Value: RainIcon)
UPDATE_DOM(Element: TempContainer, Value: Temp + "ยฐC")๐ก ๋ง๋ฌด๋ฆฌํ๋ฉฐ
๋ ์จ ์์ ฏ ๊ฐ๋ฐ์ ๊ฒ๋ณด๊ธฐ์ ๊ฐ๋จํ์ง๋ง, ์์ ์ ๋ผ์ด์ ์ค ๊ฒํ , ์๊ฒฝ๋ ์ขํ ๋ณํ, ๊ธฐ์ ๊ด์ธก ์์คํ ์ ํน์ฑ(์คํฉ vs ์๋ณด)์ ๋ํ ์ดํด๊ฐ ๋ชจ๋ ํ์ํ ๊ฒฝํ์ด์๋ค.