예측값을 현재값처럼 말하지 않는 법

forecast worker, confidence, backtest로 도착 전 판단값의 불확실성을 표시했다

주차 예측을 처음 넣으려 했을 때 가장 조심스러웠던 것은 숫자의 힘이었다. “30분 뒤 12대 남음”이라는 표현은 편리하지만, 사용자는 그 숫자를 사실처럼 읽기 쉽다.

실제로는 그렇지 않다. 원천 데이터가 늦게 갱신될 수 있고, 주차장 회전율은 시간대마다 다르고, 행사와 날씨와 통제 같은 외부 요인이 갑자기 끼어든다. 그래서 한강자리의 예측은 정답을 약속하는 기능이 아니라, 도착 전 판단에 참고할 수 있는 조심스러운 숫자로 두었다.

처음부터 큰 ML 플랫폼을 만들 생각은 없었다. 필요한 것은 모바일 앱이 빠르게 읽고, 나중에 맞았는지 확인할 수 있으며, 화면에서 과하게 단정하지 않는 예측이었다. 그래서 baseline, confidence, reason code, backtest를 한 묶음으로 봤다.

예측은 요청 전에 만들어 둔다

한강자리에는 두 종류의 예측이 있다.

예측단위목적
주차 예측주차장 단위도착 시간대의 주차 실패 위험 비교
공원 혼잡 예측공원 단위나들이 때 참고할 혼잡도와 믿을 만한 정도

공통으로 지킨 것은 같다. 요청 시점에 무거운 계산을 하지 않는다. worker가 미리 계산하고, API는 미리 만든 최신 값을 빠르게 읽어 돌려준다.

이 방식은 조금 돌아가는 길처럼 보일 수 있다. 하지만 모바일 앱에서는 요청 순간에 계산을 몰아넣는 비용이 사용자 대기 시간으로 드러난다. 예측은 “필요할 때 즉석에서 계산”하기보다, worker가 계속 만들어두고 API가 확인된 최신 결과를 읽는 쪽이 장애를 보기도 쉬웠다.

주차 예측: 현재값을 미래처럼 말하지 않기

주차 예측은 현재 잔여 대수를 복사하는 일이 아니다. 현재값, 최근 변화, 과거 같은 시간대의 변화를 함께 보고, 몇 분 뒤에 위험이 커질 수 있는지 알려주는 일이다.

주차 예측의 핵심은 가중치보다 변화의 방향이다. 현재값과 과거 변화를 어떤 순서로 보고, 근거가 약할 때 confidence와 상태를 어떻게 낮추는지가 숫자를 믿고 읽을 수 있는지를 더 크게 좌우한다.

flowchart TB
  Snapshots["주차 상태 스냅샷"] --> Buckets["5분 단위 변화"]
  Buckets --> Features["예측 특성<br/>최근 변화 · 시간대 기준선"]
  Features --> Worker["예측 워커"]
  Worker --> Run["예측 실행<br/>버전 · 만든 시각"]
  Worker --> Result["예측 결과<br/>기간 · 범위 · 위험도 · 신뢰도"]
  Result --> Cache["Redis 예측 캐시"]
  Result --> API["예측 API"]
  Cache --> API
  API --> Client["iOS 예측 화면"]

응답에는 값만 넣지 않는다. 모바일 UI가 예측을 어떻게 보여줄지 고르려면 메타데이터가 필요하다.

  • generated_at: 언제 계산했는지.
  • model_version: 어떤 로직으로 만들었는지.
  • horizon_minutes: 몇 분 뒤를 보는지.
  • risk_level: 사용자가 이해할 위험 수준.
  • failure_probability: 내부 계산을 앱에서 다룰 수준으로 정리한 확률 값.
  • confidence: 근거가 충분한지.
  • reason_codes: 왜 위험하다고 보는지 설명하는 단서.

이런 값이 있어야 앱이 “위험”, “근거 부족”, “정보 없음”을 나눠 말한다. 숫자를 하나 더 보여주는 것보다, 그 숫자를 얼마나 조심해서 읽어야 하는지 함께 내려주는 쪽이 제품에 더 가까웠다.

공원 혼잡 예측: row 없음은 한가함이 아니다

공원 혼잡은 주차장보다 더 애매하다. 사용자는 “사람이 많나?”를 알고 싶지만, 출처마다 현재값과 예측값, 갱신 시각이 다르다.

한강자리는 공원 실시간 상황과 공식 예측값을 함께 사용한다. 가까운 시간대의 예측 row가 있으면 그 값을 우선하고, 없으면 현재 상황을 fallback으로 쓴다. 이때 confidence는 낮춰야 한다.

flowchart LR
  Context["현재 공원 상황"] --> Resolver["예측 리졸버"]
  Official["공식 혼잡 예측"] --> Resolver
  Resolver --> Fresh{"목표 시간대 예측 있음?"}
  Fresh -->|예| Forecast["예측값 사용<br/>일반 신뢰도"]
  Fresh -->|아니오| Fallback["현재값 대체<br/>낮은 신뢰도"]
  Forecast --> Response["공원 예측 응답"]
  Fallback --> Response

row가 없다는 것은 한가하다는 뜻이 아니다. 정보가 없는 상태를 성공처럼 보이면 안 된다.

이 선택은 예측 전체에 반복된다. 예측이 없을 때 화면을 비워둘지, 현재값으로 대체할지, 낮은 confidence로 보여줄지는 화면에서 정할 일이다. 다만 어떤 선택을 하든 “예측 없음”을 “문제 없음”처럼 보이게 하면 안 된다.

첫 화면도 예측이 바뀌면 같이 바꾼다

예측은 별도 화면에서만 쓰이지 않는다. 한강자리의 홈 화면도 공원별 요약을 보여준다. 그래서 forecast worker는 예측을 만든 뒤 관련 cache를 무효화하고, 자주 읽히는 home summary를 다시 데운다.

sequenceDiagram
  autonumber
  participant Worker as 예측 워커
  participant PG as Postgres
  participant Redis as Redis
  participant API as 첫 화면 API
  participant App as iOS 앱

  Worker->>PG: 예측 실행과 결과 저장
  Worker->>Redis: 예측 캐시 비우기
  Worker->>Redis: 첫 화면 캐시 미리 갱신
  App->>API: 첫 화면 요약 요청
  API->>Redis: 준비된 첫 화면 값 읽기
  Redis-->>API: 캐시된 응답 반환
  API-->>App: 판단에 쓸 첫 화면 요약 반환

이 방식은 모바일 성능과 직접 연결된다. 사용자가 앱을 열 때마다 DB aggregation을 반복하지 않는다. worker가 비싼 계산을 미리 끝내고, API는 미리 만든 값을 읽는다.

장애를 볼 때도 이 방식이 편했다. 예측 run이 바뀌면 어떤 cache가 영향을 받는지 말할 수 있고, home summary가 오래됐을 때 forecast worker와 cache warmup 중 어디를 봐야 하는지도 나눠 볼 수 있다.

출시 후에는 예측을 계속 채점한다

예측은 출시했다고 끝나지 않는다. 데이터가 쌓이면 계속 채점해야 한다.

한강자리에는 forecast backtest를 위한 job과 label/metric 저장 방식이 있다. 초기에는 복잡한 계산식보다 검증 가능한 baseline이 먼저였다. 계산 방식을 바꾸려면 “더 좋아 보인다”가 아니라, 이전 버전과 비교한 metric이 있어야 한다.

내부 수식보다 나중에 다시 확인할 수 있는지가 더 오래 남는다. 어떤 계산 방식을 쓰든 run, model version, label, metric이 남지 않으면 개선 여부를 설명할 수 없다. 예측은 출시보다 출시 후에 지켜보는 시간이 더 길다.

채점할 때는 다음을 봤다.

  • 시간대별 오차가 어디서 커지는가.
  • 특정 공원이나 주차장에서 과소 예측이 반복되는가.
  • 행사·날씨·통제 같은 외부 요인이 confidence를 낮춰야 하는가.
  • 원천 데이터 장애가 예측에 어떤 영향을 주는가.
  • p10/p50/p90 범위가 실제 변동성을 설명하는가.

예측을 다르게 보게 된 점

처음에는 예측값을 만드는 일이 핵심처럼 보였다. 실제로는 입력값을 다시 만들 수 있는지, 계산한 시각과 근거가 남는지, row 없음과 낮은 혼잡도를 화면에서 다르게 말할 수 있는지가 더 오래 영향을 줬다.

모바일 앱에서는 요청 때 계산하는 똑똑함보다 미리 만든 값을 안정적으로 읽는 편이 나았다. model version과 backtest metric도 연구용 기록이 아니라, 나중에 숫자를 낮춰 말할 수 있게 해주는 근거였다.

한강자리에서 예측은 “미래를 맞히는 도구”가 아니라 “불확실성을 화면에 정직하게 옮기는 일”에 가까웠다. 그래서 숫자 하나보다 confidence와 최신성 정보가 더 오래 남았다.

이미지 확대