변경 이력 — v1 → v2
2026-08-12 · 명세 검토와 FE 피드백 반영. 큰 변경 6개, 나머지는 표.
1보드를 운행 중 / 운행 종료로 나눔
V1 — 보드는 한 모양
{
"route": { … },
"freshness": { "state": "OUT_OF_SERVICE", … },
"vehicles": [],
"stops": [ /* 전부 '차량 없음'으로 채움 */ ]
}V2 — kind로 갈림
{ "kind": "OPERATING",
"route": …, "freshness": …,
"activeVehicles": […], "stops": […] }
{ "kind": "OUT_OF_SERVICE",
"route": …,
"lastObservedAt": "2026-08-10T13:01:44Z" }운행 종료 보드의 정류장 상태는 전부 같은 채움값 — 정보가 없음. 상태가 다른 게 아니라 응답의 종류가 다른 것이라 타입으로 분리.
명세에서 보기 →2freshness 상태 enum 제거
V1 — 서버가 상태를 적음
"freshness": {
"state": "FRESH | STALE | OUT_OF_SERVICE | UNKNOWN",
"observedAt": "… | null",
"staleAt": "… | null"
}V2 — 시각만 주고 판정은 클라이언트
"freshness": {
"observedAt": "2026-08-10T22:30:12Z",
"staleAt": "2026-08-10T22:33:12Z"
}
// 낡음 = 지금 > staleAt
// 지금 = Date 헤더 + 경과 (+ Age 보정)신선도와 운행 여부는 독립 정보인데 한 enum에 묶여 있었음. 본문의 STALE은 ETag가 같으면 304만 오가 갱신되지 않는, 지켜질 수 없는 값. UNKNOWN은 생산 조건 없음 — 보드가 없으면 404.
명세에서 보기 →3features 2개로 축소
V1 — 4종, 조합이 응답과 불일치
"features": [ "SEAT_FORECAST", "BOARDING_VERDICT", "QUEUE_ESTIMATE", "UPSTREAM_RECOMMENDATION" ]
V2 — 2종
"features": [
"SEAT_FORECAST", // 판정 포함
"QUEUE_ESTIMATE"
]판정은 예보와 한 계산 — 따로 켜고 끌 수 없음. v1 예시(1650, 예보만 켜짐)는 스키마로 표현 불가한 조합. UPSTREAM_RECOMMENDATION은 v1 범위 밖.
명세에서 보기 →4방면 이름 교정
V1 — 정의와 반대
{ "id": "UP", "name": "도촌동 방면" }
{ "id": "DOWN", "name": "안양역 방면" }V2 — UP = 기점→회차
{ "id": "UP", "name": "안양역 방면" }
{ "id": "DOWN", "name": "도촌동 방면" }그대로 구현하면 사용자에게 반대 방면 표시. Direction enum에 의미 서술 추가.
명세에서 보기 →5필드명 변경
V1
"vehicles": [ … ] "queueEstimate": { "estimatedPeople": 13.7, … }
V2 — 이름이 내용을 말함
"activeVehicles": [ … ] "queueEstimate": { "pointEstimate": 13.7, … }
vehicles는 '운행 중인 차량만 담긴다'는 범위가 이름에 없음. estimatedPeople은 점추정(보조값)임이 드러나지 않아 주 표시로 오용될 여지 — 주 표시는 display.
명세에서 보기 →6발행을 행 저장으로 전환 (내부 계약)
V1 — 보드 JSON 통짜
published_boards
└ payload "{ …보드 전체… }"V2 — 행으로 발행, api는 조립만
board_publication 발행 메타 board_vehicle 차량 prediction_log 예보·판정 = 채점 로그 board_queue 대기열 board_gap 없는 자리의 사유
화면에 나간 값 = 채점 기록(같은 행). 확정된 DB 설계(v3) 반영.
pipeline 명세 보기 →그 외 변경
| 항목 | v1 | v2 | 이유 |
|---|---|---|---|
BoardRoute | — | status·turnSequence 추가 | 보드만으로 학습 중·중단 구분, 회차 지점 표시 |
InsufficientProfile | samples(integer)만 | vehicleRef 추가, samples는 가중 표본량(number) | 접근 차량 선택을 FE가 다시 하지 않게 |
SeatDataUnavailable.vehicleRef | 연결 설명 없음 | activeVehicles 연결·존재 보장 명시 | 참조 규칙 명문화 |
vehicleRef 형식 | 미정의 | pattern vehicle-[0-9]+, 위치 순 부여, 응답 간 비교 금지 | "보드 안에서만 유효"를 지키는 규칙 |
elapsedMinutes | number | integer | 발행 시 반올림. 실제 출력과 일치 |
freshness.observedAt·staleAt | nullable | non-null | null은 운행 종료뿐 → 보드 분리로 소멸. 생성 타입 변경(파괴적) |
| 대기열 UNAVAILABLE 사유 | 6종 | 5종 | OUT_OF_SERVICE는 보드 종류가 표현 |
Verdict·lowConfidence | 설명 부족 | 값별 정의 보강 | TIGHT = 줄 위치에 따라 갈림 |
/routes ETag | 규칙 없음 | 본문 콘텐츠 해시 | 재검증 구현을 하나로 |
| board 429 | 미선언 | 선언 | 목록과 같은 제한 정책 |
| routeId pattern | 최대 51자 | 40자 이내 | 저장 한도와 일치 |
| 예시 | 계산상 불가능한 조합 3건 | 재작성 + LEARNING 예시 추가 | 판정 규칙·최근접 차량 규칙에 맞게 |
| upstream 명세 | — | 실측 의미론 추가 | 좌석값은 도착(승차 전) 상태, crowded 0은 센서 없음 |
| operations 명세 | 변경 없음 | — | |
계약 반영 완료. DB 실물(v3 다이어그램·Flyway DDL) 후속 4건:
route_feature enum 축소, gap_reason에서 OUT_OF_SERVICE 제거,
gap vehicle_ref의 INSUFFICIENT_PROFILE 허용, gap samples를 number로.