diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6c0d487
--- /dev/null
+++ b/README.md
@@ -0,0 +1,74 @@
+# 날짜 정보 수집처
+
+## 기상청 API 허브
+
+-
+
+### API
+
+많은 API가 제공되나, 기상 예보를 위해서는 2개만 보면 된다.
+
+#### 초단기 예보
+
+
+
+- 제공하는 범위가 짧다. TODO: 조사 보충 필요
+- 00:30 부터 1시간 간격으로 새로운 발표를 한다.
+ - 00:30, 01:30, ..., 23:30
+- 발표 시간으로부터 15분 후에 해당 데이터를 조회할 수 있다.
+
+##### 출력 카테고리 - 초단기
+
+| 코드 | 의미 | 단위 |
+| ---- | ------------ | ---------------------------------------------------------------------- |
+| T1H | 기온 | ℃ |
+| REH | 습도 | % |
+| RN1 | 1시간 강수량 | mm |
+| SKY | 하늘상태 | 맑음(1), 구름많음(3), 흐림(4) |
+| PTY | 강수형태 | 없음(0), 비(1), 비/눈(2), 눈(3), 빗방울(5), 빗방울눈날림(6), 눈날림(7) |
+
+#### 단기 예보
+
+
+
+- 제공하는 범위가 대충 1주일 되는 것 같다. TODO: 조사 보충 필요
+- 02:00시부터 3시간 간격으로 새로운 발표를 한다.
+ - 02:00, 05:00, ..., 23:00
+- 발표 시간으로부터 10분 후에 해당 데이터를 조회할 수 있다.
+
+##### 출력 카테고리 - 단기
+
+| 코드 | 의미 | 단위 |
+| ---- | ------------ | ------------------------------------------ |
+| TMP | 1시간 기온 | ℃ |
+| TMN | 일 최저기온 | ℃ |
+| TMX | 일 최고기온 | ℃ |
+| REH | 습도 | % |
+| POP | 강수확률 | % |
+| PTY | 강수형태 | 없음(0), 비(1), 비/눈(2), 눈(3), 소나기(4) |
+| PCP | 1시간 강수량 | mm |
+| SNO | 1시간 신적설 | cm |
+| SKY | 하늘상태 | enum: 맑음(1), 구름많음(3), 흐림(4) |
+
+#### 입력 (공통)
+
+- pageNo: 페이지 번호, 1부터 시작
+- numOfRows: 한 페이지 결과 수
+ - 예시: 1000
+- dataType: 반환 형식
+ - enum: XML, JSON
+- base_date: 발표 일자, 해당 발표시간껄 보여준다는 건지? 이후라는건지? TODO: 조사 보충 필요
+ - 예시: 20251203
+- base_time: 발표 시간, 해당 발표시간껄 보여준다는 건지? 이후라는건지? TODO: 조사 보충 필요
+ - 예시: 0630
+ - API 마다 발표 시간이 정해져 있다.
+- nx: 예보지점 X 좌표
+- ny: 예보지점 Y 좌표
+- authKey: 인증키
+
+> 예보지점 (nx, ny)는 (위도, 경도)가 아니다.
+> 홈페이지에서 동네예보 지점 좌표(위경도) 참고자료를 다운로드 받아 확인할 수 있다.
+
+### 평가
+
+다소 구식 인터페이스이지만, 한국에서 사실상 유일한 공공 기상 데이터 출처이므로 필수적으로 활용해야 한다.
diff --git a/api/main.tsp b/api/main.tsp
index 642424e..4519a27 100644
--- a/api/main.tsp
+++ b/api/main.tsp
@@ -1,44 +1,82 @@
import "@typespec/http";
using Http;
-@service(#{ title: "Widget Service" })
-namespace DemoService;
+@service(#{ title: "Seven Skies Service" })
+namespace SevenSkiesService;
-model Widget {
- id: string;
- weight: int32;
- color: "red" | "blue";
+@doc("섭씨 온도(℃)")
+scalar Celsius extends numeric;
+
+enum WeatherCondition {
+ @doc("맑음") Clear: "CLEAR",
+ @doc("구름많음") Cloudy: "CLOUDY",
+ @doc("비") Rain: "RAIN",
+ @doc("눈") Snow: "SNOW",
+ @doc("비/눈") RainSnow: "RAIN_SNOW",
}
-model WidgetList {
- items: Widget[];
+model DailyForecast {
+ @doc("해당 날짜")
+ date: offsetDateTime;
+
+ @doc("하루 동안의 예상 최고 기온(℃)")
+ high: Celsius;
+
+ @doc("하루 동안의 예상 최저 기온(℃)")
+ low: Celsius;
+
+ @doc("하루 전체를 대표하는 날씨 상태")
+ condition: WeatherCondition;
+}
+
+model DailyForecastList {
+ items: DailyForecast[];
+}
+
+model HourlyForecast {
+ @doc("예상 시간")
+ time: offsetDateTime;
+
+ @doc("1시간 동안의 예상 기온(℃)")
+ temperature: Celsius;
+
+ @doc("1시간 동안의 예상 날씨 상태")
+ condition: WeatherCondition;
+}
+
+model HourlyForecastList {
+ items: HourlyForecast[];
}
@error
model Error {
- code: int32;
- message: string;
+ @doc("에러 코드")
+ errorCode: int32;
+
+ @doc("에러 메시지")
+ errorMessage: string;
}
-model AnalyzeResult {
- id: string;
- analysis: string;
-}
+@route("/forecasts")
+@tag("Forecasts")
+interface Forecasts {
+ @get
+ @route("/daily")
+ listDaily(): {
+ @statusCode statusCode: 200;
+ @body body: DailyForecastList;
+ } | {
+ @statusCode statusCode: 500;
+ @body body: Error;
+ };
-@route("/widgets")
-@tag("Widgets")
-interface Widgets {
- /** List widgets */
- @get list(): WidgetList | Error;
- /** Read widgets */
- @get read(@path id: string): Widget | Error;
- /** Create a widget */
- @post create(@body body: Widget): Widget | Error;
- /** Update a widget */
- @patch update(@path id: string, @body body: MergePatchUpdate): Widget | Error;
- /** Delete a widget */
- @delete delete(@path id: string): void | Error;
-
- /** Analyze a widget */
- @route("{id}/analyze") @post analyze(@path id: string): AnalyzeResult | Error;
+ @get
+ @route("/hourly")
+ listHourly(): {
+ @statusCode statusCode: 200;
+ @body body: HourlyForecastList;
+ } | {
+ @statusCode statusCode: 500;
+ @body body: Error;
+ };
}