공식 주차 API를 바탕으로 짠 계획입니다. 계획을 세우면서 모델 입력으로 쓸 추가 공공 API 두 개(시간대별 예상 승객 수, 공휴일)도 확인했습니다.
대시보드가 답할 질문: "내가 ○시에 도착하면 어느 주차장에 자리가 있을까?", 그리고 "그 예측은 실제로 얼마나 맞았나?"
초기 목표치 (4주 운영 후 실제 데이터로 다시 정합니다) → 우선은 모으는 것에 집중합니다. 아래는 임의로 지정한 지표와 목표입니다.
| 지표 | 목표 |
|---|---|
| 1시간 뒤 예측 오차(점유율, 평균) | 3%p 이하 |
| 3시간 뒤 예측 오차(점유율, 평균) | 5%p 이하 |
| 예측 범위(하위 10%~상위 10%) 안에 실제값이 들어온 비율 | 약 80% |
| "만차 위험" 경보가 실제 만차를 잡아낸 비율 | 80% 이상 |
| 수집 성공률 | 월 99% 이상 (빠진 5분 구간 1% 미만) |
공공데이터포털 서비스키 하나로 아래 API를 모두 신청할 수 있습니다.
| 용도 | API | 호출 주기 | 하루 호출 수 |
|---|---|---|---|
| 예측 대상: 구역별 주차 대수와 총 면수 | B551177/StatusOfParking/getTrackingParking | 5분 | 288 |
| 선행 지표: 시간대별 예상 출·입국 승객 (오늘·내일) | B551177/passgrAnncmt/getPassgrAnncmt | 1시간 | 약 48 |
| 공휴일·연휴 달력 | 한국천문연구원 특일 정보 | 월 1회 | 약 0 |
| (선택) 날씨 | 기상청 단기예보 | 3시간 | 8 |
구역 유형별로 움직이는 방식이 다릅니다.
flowchart LR
API["data.go.kr<br/>주차 · 승객예고 · 특일"] --> C["collect<br/>5분"]
API --> X["exog<br/>1시간"]
C --> RAW[("Blob raw/<br/>원본 JSON")]
C --> OBS[("Blob obs/<br/>일별 JSONL")]
X --> EXO[("Blob exog/")]
OBS --> FC["forecast<br/>15분"]
EXO --> FC
FC --> FCS[("Blob forecasts/<br/>발행분 불변 저장")]
FCS --> EV["evaluate + publish<br/>1시간 · 매일"]
OBS --> EV
EV --> PUB[("public/*.json")]
C --> PUB
PUB --> WEB["Static Web Apps<br/>ECharts 대시보드"]
C -.-> AI["App Insights<br/>20분 무수집 알림"] |
| 구성요소 | 선택 | 예상 월 비용 |
|---|---|---|
| 실행 | Azure Functions Flex Consumption (Python 3.12), 함수 4개 | 무료 제공량 내외, $0~3 |
| 저장소 | Blob Storage (1년치가 수백 MB 수준) | $0.5 미만 |
| 비밀값 | Key Vault (서비스키) | 약 $0.03 |
| 모니터링 | App Insights (하루 사용량 상한 설정) | $0~2 |
| 웹 | Static Web Apps Free | $0 |
| 배포 | GitHub + Actions(OIDC 인증) + Bicep | $0 |
비용은 합쳐서 월 $1~5 정도로 추정합니다.
이렇게 짠 이유
검토했지만 뺀 선택지
| 선택지 | 뺀 이유 |
|---|---|
| GitHub Actions 예약 실행 | 실행이 수 분~수십 분 밀리거나 건너뛰는 일이 흔해 5분 수집에 맞지 않음 |
| Azure SQL serverless | 5분마다 쓰기가 들어와 자동 일시정지가 안 돼서 비용이 커짐 |
| ADX 무료 클러스터 | 시계열 분석 기능은 강하지만 외부 공개가 안 됨. 개인 분석용 보조로는 쓸 수 있음 |
| Power BI 웹에 게시 | 사내 정책상 보통 막혀 있음 |
먼저 확인할 위험: MCAP 구독에 Blob 공개 접근을 막는 정책이 걸려 있을 수 있습니다. 그렇다면 Function의 HTTP 엔드포인트가 managed identity로 Blob을 읽어 JSON을 캐시와 함께 내주는 방식으로 바꿉니다. 0단계에서 확인합니다.
| 테이블 | 주요 컬럼 |
|---|---|
zones (구역 목록) | zone_id, 터미널, 유형(단기/장기/타워/예약), 원본 구역명·별칭, 사용 기간 |
observations (실측) | 5분 구간 시각(KST), zone_id, 주차 대수, 총 면수, 가용 대수, 점유율, 원본 갱신 시각(datetm), 수집 시각, 품질 표시 |
raw_calls (호출 기록) | 호출 시각, HTTP 상태, 결과 코드, 응답 시간, 원본 파일 경로 |
calendar (달력) | 날짜, 요일, 공휴일 여부, 연휴 묶음 번호, 연휴 며칠째, 연휴까지 남은 날 |
exog_passenger (예상 승객) | 날짜, 시간, 터미널, 예상 출국·입국 승객, 가져온 시각 |
forecasts (예측, 덮어쓰지 않음) | 발행 시각, 모델·버전, zone_id, 대상 시각, 몇 분 뒤 예측인지, 점유율 예측(중앙값·하위 10%·상위 10%), 가용 대수 예측, 만차 확률 |
accuracy (정확도 집계) | 날짜, 모델, 구역, 예측 거리 구간, 요일·시간, 평균 오차, 편향, 적중률, 표본 수 |
collect, 5분마다)numOfRows=100, type=json으로 호출합니다. 타임아웃은 10초, 실패하면 2초·5초·10초 간격으로 세 번까지 다시 시도합니다.raw/parking/YYYY/MM/DD/HHmm.json에 저장합니다. 나중에 가공을 다시 하거나 점검할 때 씁니다.floor)을 구역 목록과 맞춥니다. 목록에 없는 이름이 나오면 알림을 보냅니다(신규 주차장이나 이름 변경 감지).STALE: 원본 갱신 시각이 직전과 같음 (원천 데이터가 멈춤)OVER: 주차 대수가 총 면수보다 많음CAPCHANGE: 총 면수가 바뀜 (공사 등)GAP: 수집이 빠진 구간obs/YYYY/MM/DD.jsonl에 이어 붙이고, 중복은 읽을 때 걸러냅니다. public/latest.json도 갱신합니다.예측 대상은 구역별 점유율입니다. 총 면수는 공사 등으로 바뀔 수 있어서, 점유율로 예측한 뒤 가용 대수로 바꿉니다. 발행은 15분마다, 15분 간격으로 24시간 뒤까지입니다.
| 모델 | 방법 | 쓸 수 있는 시점 |
|---|---|---|
| M0 | 지난주 같은 요일·같은 시각 값 (비교 기준선) | 수집 1주 뒤 |
| M1 | 최근 4~8주 같은 요일·같은 15분 구간의 가중 중앙값과 상·하위 10% 범위 | 2주 뒤 (4주 뒤 안정) |
| M1b | M1 × (오늘 예상 승객 ÷ 같은 요일 평균 예상 승객) | 2주 뒤, 연휴 대응 |
| M2 | M1에 "지금 평소보다 얼마나 다른지"를 더하고, 먼 시간일수록 그 차이를 줄여 반영 | 2주 뒤, 다가오는 몇 시간 예측의 핵심 |
| M3 | LightGBM 분위수 회귀. 입력: 시간대, 요일, 연휴 정보, 예상 승객, 최근 값(현재·1시간 전·어제·지난주) | 8~12주 뒤 |
annotations.yaml에 직접 적습니다.| 페이지 | 보여줄 내용 |
|---|---|
| 앞으로 24시간 (메인) | T1/T2 탭. 오늘 실측(실선), 예측 중앙값(점선), 예측 범위(띠)를 겹쳐 표시하고 현재 시각에 세로선. 만차 위험 시간대 강조. 도착 시각 선택 → 여유 예상 구역 순위와 만차 확률, 최근 정확도를 보여주는 신뢰도 표시 |
| 요일 × 시간 패턴 | 최근 8주 기준 7×24 히트맵(점유율 / 만차 확률 / 가용 대수 전환), 요일별 하루 흐름 비교 |
| 예측 정확도 | 어제·오늘 예측 대 실제 겹쳐 보기, 예측 거리별 오차 추이, 요일×시간 오차 히트맵, 모델 비교, 특이 상황 기록 |
| 장기 추이 | 하루 최고 점유율 달력 히트맵, 연휴 3일 전~3일 후 겹쳐 보기, 주간 평균 추이 |
| 방법·출처 | 출처 표시("인천국제공항공사, 공공데이터포털"), 비공식 서비스라는 안내, 데이터 수집률 |
| 파일 | 갱신 주기 | 내용 |
|---|---|---|
latest.json | 5분 | 구역별 최신값과 오늘 5분 단위 흐름 |
forecast.json | 15분 | 앞으로 24시간 예측 |
accuracy.json | 1시간 | 정확도 지표 |
pattern.json | 매일 | 요일×시간 통계 |
history_daily.json | 매일 | 하루 단위 최고·평균 점유율 |
icn-parking-forecast/
infra/ Bicep (main + modules)
functions/ function_app.py, collect/, exog/, features/, models/, evaluate/, publish/
web/ index.html, app.js, charts/, i18n/
config/ zones.yaml, annotations.yaml
notebooks/ 데이터 탐색
tests/
.github/workflows/ infra.yml, functions.yml, web.yml
| 단계 | 기간 | 산출물 | 완료 기준 |
|---|---|---|---|
| 0단계 | 오늘~내일 | API 키 3개 발급, 첫 응답으로 구역 목록 확정, 리소스 그룹 결정, Blob 공개 정책 확인 | 실제 응답을 받음 |
| 1단계 (최우선) | 1~3일 | 수집기 가동, 원본 저장, 알림 | 24시간 동안 288번 모두 수집 |
| 2단계 | 1~2주 | 대시보드 첫 버전: 오늘 실측 + 어제·지난주 겹쳐 보기, 수집 현황. M0 예측 발행 시작 | 공개 URL로 접속 가능 |
| 3단계 | 2~4주 | M1·M1b·M2, 정확도 페이지, 요일×시간 히트맵, 운영 계정 신청 | 예측 거리별 정확도가 매일 갱신됨 |
| 4단계 | 6~12주 | M3, 만차 확률 보정, 7일 전망 | 운영 모델이 M0보다 20% 이상 정확 |
| 5단계 | 3개월 이후 | 장기 추이·연휴 비교, 전날 밤 "내일 만차 위험" 요약 알림 | 연휴 3회 이상 평가 완료 |
⚠️ 1단계를 서두를 이유: 과거 데이터는 나중에 소급해서 받을 수 없습니다. 이번 주말 개천절 연휴(10/35)나 한글날 연휴(10/911) 전에 수집을 시작해야 첫 연휴 자료를 확보할 수 있습니다.
| 위험 | 대응 |
|---|---|
| API 장애·지연 | 재시도, 누락 구간 표시, 알림. 대시보드에 "데이터 지연" 표시 |
| 구역 구성 변경 (신규 주차장, 공사) | 구역 목록에 없는 이름이 오면 알림, 면수 변경 표시, 구역 목록 이력 관리 |
| 연휴 데이터 부족 | 예상 승객 수로 보정(M1b), 1년 뒤 연휴 전용 모델로 전환 |
| 예측을 공식 정보로 오해 | "비공식 · 예측값" 안내 문구. 예상 승객 수를 화면에 보여줄 경우 API 설명에 따라 공항공사 관리부서와 안내 문구 협의 필요 (입력으로만 쓰면 협의 부담이 작음) |
| 개발 계정 한도나 기간 만료 | 공개 시점에 운영 계정으로 전환 (자동 승인) |
| 비용 초과 | App Insights 하루 사용량 상한, 예산 알림 |
계획은 세션의 plan.md에 저장했고, 이 프로젝트 내용은 기억해 두었습니다. 1단계를 서둘러야 하는 만큼 다음 진행 방식을 골라 주세요. API 키는 공공데이터포털에 로그인해서 직접 발급받으셔야 하고, 나머지 코드와 인프라는 제가 준비할 수 있습니다.