Packages

기상청 Open API 3종(단기예보, 중기예보, 생활기상지수)의 타입 안전 Gleam 래퍼. 12개 엔드포인트, 자동 격자 변환, 패턴 매칭 가능한 응답 타입 제공.

Current section

Files

Jump to
malgleam README.md
Raw

README.md

# malgleam (맑을림)
[![Package Version](https://img.shields.io/hexpm/v/malgleam)](https://hex.pm/packages/malgleam)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/malgleam/)
기상청 Open API 3종의 타입 안전 Gleam 래퍼. Erlang/JavaScript 양쪽 타겟 지원.
```sh
gleam add malgleam@1
```
## 왜 malgleam인가?
기상청 API를 직접 호출하려면 격자 변환 공식, 카테고리 코드표, 지역번호 목록, XML/JSON 파싱을 모두 직접 처리해야 합니다. malgleam은 이 모든 번거로움을 타입 시스템 뒤에 숨깁니다.
| 직접 호출 시 | malgleam 사용 시 |
|-------------|-----------------|
| 서울 위경도를 Lambert Conformal Conic 공식으로 격자 변환 | `location.seoul` |
| `"T1H"`, `"PTY"`, `"SKY"` 코드표 참조 | `obs.temperature`, `obs.precipitation_type` |
| 하늘상태 `"1"` → 맑음 매핑 | `case obs { Clear -> ... }` |
| 중기육상예보 구역코드 `"11B00000"` 조회 | `region.land_seoul_incheon_gyeonggi` |
| 생활기상지수 행정구역코드 `"1100000000"` 조회 | `area.seoul` |
| URL 조립 + JSON 파싱 + 에러코드 처리 | 요청 빌더 + 디코더가 전부 처리 |
## 빠른 시작
### 서울의 현재 날씨
```gleam
import malgleam/short_term
import malgleam/location
import gleam/httpc
let request = short_term.ultra_srt_ncst(
service_key: "my-service-key",
location: location.seoul,
base_date: "20240701",
base_time: "0600",
)
let assert Ok(response) = httpc.send(request)
let assert Ok(obs) = short_term.decode_ultra_srt_ncst(response)
obs.temperature // 24.5 (℃)
obs.humidity // 78 (%)
obs.precipitation_type // NoPrecipitation
obs.wind_direction // W (16방위)
obs.wind_speed // 2.1 (m/s)
```
8개 관측 카테고리(T1H, RN1, UUU, VVV, REH, PTY, VEC, WSD)가 **의미 있는 필드명의 단일 레코드**로 반환됩니다.
### 위경도로 위치 지정
```gleam
let busan = location.from_coords(lat: 35.1796, lng: 129.0756)
let request = short_term.vilage_fcst(
service_key: "my-key",
location: busan,
base_date: "20240701",
base_time: "0500",
)
```
Lambert Conformal Conic 격자 변환이 자동으로 처리됩니다.
### 단기예보 해석
```gleam
let assert Ok(items) = short_term.decode_vilage_fcst(response)
// 시간별 그룹핑
let groups = short_term.group_by_time(items)
// [#("20240701", "0600", [TMP항목, SKY항목, ...]),
// #("20240701", "0900", [...]), ...]
// 개별 아이템 해석
short_term.parse_sky(sky_item) // Ok(Clear)
short_term.parse_float_value(tmp_item) // Ok(25.0)
short_term.parse_wind_direction(vec_item) // Ok(S)
```
### 중기예보
```gleam
import malgleam/mid_term
import malgleam/region
let request = mid_term.mid_land_fcst(
service_key: "my-key",
region: region.land_seoul_incheon_gyeonggi,
forecast_time: "202407010600",
)
let assert Ok(response) = httpc.send(request)
let assert Ok(forecast) = mid_term.decode_mid_land_fcst(response)
// days 4~7은 AM/PM, days 8~10은 하루 단위
list.each(forecast.days, fn(day) {
case day.weather_am {
Some(MidClear) -> "오전 맑음"
Some(MidOvercastWithRain) -> "오전 비"
_ -> "..."
}
})
```
지역 코드를 외울 필요가 없습니다. 잘못된 코드 타입은 **컴파일 타임에 차단**됩니다.
```gleam
// OK: mid_land_fcst에 LandRegionId 전달
mid_term.mid_land_fcst(key, region.land_seoul_incheon_gyeonggi, time)
// 컴파일 에러: SeaRegionId는 mid_land_fcst에 전달 불가
mid_term.mid_land_fcst(key, region.sea_west_central, time)
```
### 생활기상지수
```gleam
import malgleam/living_index
import malgleam/area
let request = living_index.uv_idx(
service_key: "my-key",
area: area.seoul,
time: "2024070618",
)
let assert Ok(response) = httpc.send(request)
let assert Ok(uv) = living_index.decode_uv_idx(response)
uv.tomorrow // Some(8) — 내일 자외선지수 매우높음
```
시간별 지수 조회:
```gleam
let assert Ok(freeze) = living_index.decode_freeze_idx(response)
living_index.find_hourly(freeze.hourly, 6) // Some(75) — 6시간 후 동파 위험 높음
```
### 선택 파라미터는 파이프라인으로
```gleam
let request = short_term.vilage_fcst(key, location.seoul, "20240701", "0500")
|> short_term.with_rows(100)
|> short_term.with_page(2)
```
## 지원 엔드포인트 (12개)
### 단기예보 (`malgleam/short_term`)
| 함수 | API | 설명 | 반환 타입 |
|------|-----|------|----------|
| `ultra_srt_ncst` | getUltraSrtNcst | 초단기실황 | `Observation` |
| `ultra_srt_fcst` | getUltraSrtFcst | 초단기예보 | `List(ForecastItem)` |
| `vilage_fcst` | getVilageFcst | 단기예보 | `List(ForecastItem)` |
| `fcst_version` | getFcstVersion | 예보버전 | `List(ForecastVersion)` |
### 중기예보 (`malgleam/mid_term`)
| 함수 | API | 설명 | 반환 타입 |
|------|-----|------|----------|
| `mid_fcst` | getMidFcst | 중기전망 | `MidOutlook` |
| `mid_land_fcst` | getMidLandFcst | 중기육상예보 | `MidLandForecast` |
| `mid_ta` | getMidTa | 중기기온 | `MidTempForecast` |
| `mid_sea_fcst` | getMidSeaFcst | 중기해상예보 | `MidSeaForecast` |
### 생활기상지수 (`malgleam/living_index`)
| 함수 | API | 설명 | 반환 타입 |
|------|-----|------|----------|
| `freeze_idx` | getFreezeIdxV2 | 동파가능지수 | `FreezeIndex` |
| `uv_idx` | getUVIdxV2 | 자외선지수 | `UvIndex` |
| `air_diffusion_idx` | getAirDiffusionIdxV2 | 대기확산지수 | `AirDiffusionIndex` |
| `sen_ta_idx` | getSenTaIdxV2 | 체감온도 | `SensibleTemperature` |
## 설계 원칙
### HTTP 클라이언트 독립
모든 요청 빌더는 `gleam/http/request.Request(String)`를 반환합니다. HTTP 클라이언트를 자유롭게 선택할 수 있습니다.
- Erlang: `gleam_httpc`
- JavaScript: `gleam_fetch`
### Result 기반 에러 처리
모든 디코더는 `Result(T, ApiError)`를 반환합니다.
```gleam
case short_term.decode_vilage_fcst(response) {
Ok(items) -> // 성공
Error(ApiServiceError(InvalidRequestParameter, msg)) -> // API 에러
Error(JsonDecodeError(msg)) -> // JSON 파싱 실패
Error(UnexpectedResponse(body)) -> // 예상치 못한 응답
}
```
기상청 API 에러코드(01~99) 전체가 `ErrorCode` variant로 매핑되어 있습니다.
### 주요 도시 상수
위치, 지역, 지점 코드를 상수로 제공합니다.
```gleam
// 단기예보 — 18개 도시 격자좌표
location.seoul location.busan location.daegu location.jeju ...
// 중기예보 — 지점번호, 육상/해상/기온 구역코드
region.seoul_incheon_gyeonggi // StationId
region.land_seoul_incheon_gyeonggi // LandRegionId
region.temp_seoul // TempRegionId
region.sea_west_central // SeaRegionId
// 생활기상지수 — 17개 시도 행정구역코드
area.seoul area.busan area.gyeonggi area.jeju ...
```
## Development
```sh
gleam build # 빌드
gleam test # 테스트
gleam format # 포맷
gleam docs build # API 문서 생성
```