첫 화면 값을 한 응답으로 묶기: home-summary
첫 화면의 주차, 나들이, 예측과 출처 상태를 한 번에 내려주는 API 계약
한강자리 첫 화면에는 주차장, 현재 값, 예측, 혼잡도, 행사, 시설 요약, 길찾기 후보와 출처 확인 결과가 함께 들어온다. 클라이언트가 여러 응답을 조합하면 섹션마다 성공 여부와 갱신 시각이 어긋날 수 있다.
주차 섹션은 방금 갱신됐는데 행사 섹션은 오래된 값일 수 있다. 예측은 다른 horizon을 사용하고 위젯에는 앱과 다른 갱신 상태가 표시될 수도 있다.
서버는 첫 화면에 필요한 값을 한 응답으로 묶어 섹션과 위젯의 기준 시각을 맞춘다.
한 번에 받는 첫 화면 값
home-summary는 한 공원에 대해 앱과 위젯이 처음 표시할 값을 한 번에 제공한다.
flowchart LR API["공원별 첫 화면 응답"] --> Park["공원"] API --> Parking["주차 집계<br/>주차장 상태"] API --> Forecast["주차 예측<br/>공원 혼잡 예측"] API --> Outing["나들이 요약<br/>신호 · 시설 그룹 · 길찾기"] API --> Freshness["갱신 상태<br/>출처 확인"]
개별 호출 방식에서는 주차장 목록, 현재 값 목록, 예측 overview, 나들이 overview와 출처 확인 결과를 따로 불러야 한다. 이때 다음 문제가 생긴다.
- 호출별 성공/실패가 서로 어긋난다.
- 화면 일부가 이전 공원 값으로 남을 수 있다.
- 위젯은 여러 네트워크 호출을 감당하기 어렵다.
- cache와 갱신 상태 확인이 복잡해진다.
- API가 바뀔 때 iOS decoding에서 방어해야 할 범위가 넓어진다.
서버는 한 payload 안에서 각 섹션의 값과 갱신 상태를 함께 결정한다.
home-summary의 필드는 “첫 화면에서 바로 필요한가”를 기준으로 정했다. 상세 화면에서만 필요한 긴 목록이나 원문 payload를 제외해 응답의 크기와 변경 범위를 제한한다.
네 묶음으로 나눈 응답
iOS에서 받는 값은 크게 네 부분으로 나뉜다.
| 필드 | 역할 |
|---|---|
park | 현재 summary가 어떤 공원을 말하는지 확인 |
parking | 주차 aggregate, 추천 주차장, lots, statuses |
forecast | horizon과 주차/공원 혼잡 예측 overview |
outing | 일반 화면 신호, 시설 그룹, 길찾기, 출처 확인 결과 |
앱의 주차 화면은 HomeSummary에서 주차 overview를 꺼내고 일반 화면은 compact data를 재사용한다.
flowchart TB HomeSummary --> ParkingOverview["ParkingOverview 값"] HomeSummary --> OutingOverview["일부 OutingOverview 값"] HomeSummary --> WidgetParking["주차 위젯 스냅샷"] HomeSummary --> WidgetGeneral["일반 위젯 스냅샷"]
클라이언트는 서버가 묶어 준 값을 화면별로 나눠 쓴다. 주차 overview, 일반 공원 summary와 위젯 snapshot이 같은 응답에서 출발하므로 중복 호출과 화면 전환 중 시점 차이가 줄어든다.
기존 앱의 새 응답 호환성
iOS 앱은 서버보다 느리게 업데이트되므로 이전 앱이 새 서버 payload를 읽는 기간이 생긴다. home-summary는 이 기간에 기존 필드 의미를 유지한다.
지켜야 할 것은 다음이다.
- 기존 required field를 유지한다.
- 새 값은 optional field 또는 nested object 추가로 시작한다.
- enum은 알 수 없는 값을 만났을 때의 동작을 갖는다.
- 날짜와 최신성 관련 field는 의미를 바꾸지 않는다.
- park ID가 요청한 값과 다르면 payload를 믿지 않는다.
위젯 로더도 이 방식을 따른다. 네트워크에서 받은 home-summary의 공원 ID가 요청과 다르면 빈 데이터로 처리해 캐시 오염을 막는다.
첫 화면 응답을 나누는 iOS 캐시
iOS는 받은 HomeSummary를 SwiftData에 저장하고 내부의 공원, 주차장, status와 forecast overview를 각 cache에 반영한다.
sequenceDiagram
autonumber
participant Store as 앱 상태 스토어
participant Repo as 주차 저장소
participant API as 한강자리 API
participant Cache as SwiftData 캐시
Store->>Repo: 저장된 첫 화면 값 확인
alt 캐시 최신
Repo-->>Store: 캐시된 첫 화면 값 반환
else 오래됐거나 없음
Repo->>API: 첫 화면 응답 요청
API-->>Repo: 첫 화면 응답 반환
Repo->>Cache: 첫 화면 값 저장
Repo->>Cache: 공원/주차/상태/예측 캐시 갱신
Repo-->>Store: 새 첫 화면 값 반환
end
첫 화면과 상세 화면은 같은 응답에서 출발한다. 사용자가 주차 상세로 들어가도 방금 받은 값을 재사용한다.
서버 cache의 최신성 표시
서버는 home-summary에 빠른 응답용 cache와 마지막 성공 응답 cache를 둔다. 정상적으로 다시 만들면 두 cache를 같이 갱신한다. 다시 만들기에 실패하면 마지막 성공 응답을 반환할 수 있다.
HTTP 응답에는 Cache-Control: private와 ETag를 붙여 같은 앱 인스턴스의 짧은 반복 요청에 304로 응답할 수 있게 한다.
stale backup을 반환할 때도 payload의 갱신 상태와 출처 확인 결과를 유지한다.
앱과 위젯의 기준 시각
home-summary는 앱 화면, 위젯과 cache가 함께 쓰는 첫 화면 값을 한 응답으로 제공한다. 클라이언트는 요청한 공원과 응답 공원 ID를 검증하고 새 필드가 추가돼도 기존 의미를 유지한다. 서버가 stale cache를 반환하면 갱신 상태를 보존해 마지막 확인값임을 표시한다.
Comments
아직 댓글이 없습니다. 첫 댓글을 남겨주세요.
검토 대기 중