프론트엔드 변경 이력 — v4 → v4-1
프론트엔드 크루가 읽는 문서다. v4에서 v4-1로 넘어가며 화면과 코드가 실제로 바뀌는 것만 담았다. 이전 개정(v3 → v4, 2026-08-19)의 이력은 전체 변경 이력에 있다. 백엔드 쪽 변경은 BE 변경 이력에 따로 있다.
2026-08-31 — 계약 v4-1 (2.1.0, 확정)
1오류 응답에서 retryable 이 빠졌다 — 3필드
오류 응답은 code · message · requestId 셋이다.
재시도 여부는 응답에 담지 않는다 — 상태 코드와 Retry-After 유무로 판단한다.
{
"code": "ROUTE_NOT_FOUND",
"message": "등록되지 않은 노선입니다.",
- "requestId": "req-01J9X2ABCE",
- "retryable": false
+ "requestId": "req-01J9X2ABCE"
}
깨지는 변경이다. retryable 을 읽는 코드는 지우고, 재시도 판단은 이렇게 바꾼다.
값이 상태 코드에서 유도되는 중복이라 응답에서 뺐다.
- if (error.retryable) retry(); + if (res.status === 503 && res.headers.has("Retry-After")) retry();
2오류 코드 5종 → 9종
전에는 없는 경로·틀린 메서드·미처리 예외가 계약 밖 모양(프레임워크 기본 응답)으로 샜다. 이제 무엇이 와도 위 3필드 한 모양으로 온다.
INVALID_ROUTE_ID 400 ROUTE_NOT_FOUND 404 MODEL_OUT_OF_SCOPE 503 NO_RECENT_OBSERVATION 503 SERVICE_UNAVAILABLE 503 + INVALID_REQUEST 400 경로 파라미터 밖의 형식 오류 + ENDPOINT_NOT_FOUND 404 없는 경로 (예: GET /api/v1/route — s 빠짐) + METHOD_NOT_ALLOWED 405 지원하지 않는 메서드 (예: POST /api/v1/routes) + INTERNAL_ERROR 500 서버가 요청을 처리하지 못함
추가만이라 무해하다. ErrorCode 타입을 닫아 둔 클라이언트만 네 값을 더한다.
화면 분기가 필요 없는 코드는 공통 오류 화면으로 보내면 된다.
3보드 응답에 staleAt 추가
전에는 "언제부터 낡은 보드인가"의 기준(관측 + 5분)이 서버 안에만 있었다.
이제 응답이 직접 말한다. /vehicles 의 observation.staleAt 과 같은 이름, 같은 뜻이다.
{
"observedAt": "2026-08-31T13:00:00+09:00",
+ "staleAt": "2026-08-31T13:05:00+09:00",
"model": { … },
추가만이라 무해하다. 5분을 하드코딩했다면 이 값으로 바꾼다 — 정책이 바뀌어도 화면 수정이 없다. (SAL-113)
+ const stale = Date.now() > Date.parse(board.staleAt); // true 면 낡음 표시
4observation.state — 읽어낸 관측이 0행이면 UNKNOWN
상류가 차량 행을 줬는데 서버가 하나도 못 읽어낸 경우의 판정이 갈렸다.
상황: 상류는 차량 행을 줬는데, 서버가 한 행도 못 읽었다 - "observation": { "state": "NO_VEHICLES_OBSERVED", … } ← "차가 없다"로 보인다 + "observation": { "state": "UNKNOWN", … } ← "모른다"가 정직하다
상류가 필드 형식을 바꾸면 그때부터 모든 행이 걸러지는데, 그것이 "0대 운행"으로 보이면
화면도 운영도 이상을 알아챌 수 없다. enum 은 그대로다 — UNKNOWN 화면 처리가 이미 있으면 할 일이 없다.
5sequence − horizonStops 는 현재 순번이 아니다 — 본문 아홉 곳 정정
horizonStops 의 정의부터 정확히 하면 —
목표 정류장 순번에서, 그 버스가 지나온 정류장 순번(통과 순번)을 뺀 값이다.
버스가 어느 정류장에 도착 중(phase: ARRIVING)이면 그 정류장은 아직 안 지난 것이라,
통과 순번은 현재 순번보다 1 작다. 여기서 어긋남이 생긴다.
순번: 5 6 7 8 9 10
노선: ---o------o------B------o------o------G--->
B = 버스 (7번에 도착 중, phase ARRIVING)
G = 목표 정류장 (sequence 10)
지나온 정류장(통과 순번) = 6 7번에는 "도착 중"이라 아직 안 지났다
horizonStops = 10 − 6 = 4 앞으로 지날 곳: 7 · 8 · 9 · 10 네 칸
옛 본문의 역산: 10 − 4 = 6 ← 6은 "지나온" 정류장이다
버스의 실제 위치: 7 ← 지도에 그리면 한 칸 뒤에 찍힌다
같은 버스라도 상태에 따라 갈린다 — 어긋나는 것은 도착 중일 때뿐이다.
| 버스 상태 (7번 정류장 기준) | currentStopSequence | 통과 순번 | horizonStops(목표 10) | 10 − horizonStops | 실제 위치와 |
|---|---|---|---|---|---|
7번에 도착 중 ARRIVING | 7 | 6 | 4 | 6 | 1 작다 |
7번에서 출발 DEPARTED | 7 | 7 | 3 | 7 | 일치 |
7→8 이동 중 IN_TRANSIT | 7 | 7 | 3 | 7 | 일치 |
이 1칸이 제일 아픈 곳이 회차 판정이다. 경계에서 방향이 뒤집힌다.
회차 순번(route.turnSequence) = 43 버스: 44번 정류장에 도착 중 → 실제로는 회차를 이미 지났다 (44 > 43) - 옛 판정: 55(목표 sequence) − 12(horizonStops) = 43 ≤ 43 → "아직 회차 전" ← 오판 + 새 판정: /vehicles 의 currentStopSequence 44 > 43 → "회차 지남" ← 맞다
그래서 본문 아홉 곳을 이렇게 바로잡았다.
- `sequence − horizonStops` 가 그 차량의 현재 순번이다 + `sequence − horizonStops` 는 그 차량이 지나온 정류장 순번이다. + 도착 중인 차량은 현재 순번보다 1 작다. + 버스 위치·회차 판정은 /vehicles 의 currentStopSequence 로 한다.
스키마(minimum 1 · maximum 12)와 값은 그대로다 — 서버가 주는 숫자는 전에도 지금도 옳았고,
틀린 것은 그 숫자를 읽는 법을 적은 본문이었다. 본문대로 역산해 지도를 그렸다면
도착 차량 1칸 오차와 회차 오판이 이번 정정으로 사라진다.
두 응답은 vehicleId 가 같은 값이라 그대로 이어 붙일 수 있다.
6info.version 2.0.0 → 2.1.0
info: - version: 2.0.0 + version: 2.1.0
1·2·3이 스키마 변경이라 마이너를 올렸다.
전체 변경 이력 →이 페이지는 최신 개정 하나만 담는다. 지난 개정 이력(v3 → v4 · 2026-08-19 팀 검토 반영 · 데모 재적합)은 전체 변경 이력으로 옮겼다.