#[derive(Debug, Deserialize)]
struct TrainPositionResponse { ... } // 원본 API 응답
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrainPositionResult { ... } // 클라이언트 응답
So what: API 원본 응답 구조체와 클라이언트에 내려줄 구조체를 분리했다
So why: 서울 열린데이터 API는 한국어 축약형 필드명을 쓴다. 이걸 그대로 Flutter에 내려주면 Flutter팀이 필드 의미를 파악하기 어렵다. 원본은 파싱용으로만 쓰고, TrainPositionResult에서 의미 있는 이름으로 변환해서 보내면 된다.
client: Client::builder()
.timeout(Duration::from_secs(10))
.build()
.expect("HTTP 클라이언트 생성 실패"),
So what: HTTP 클라이언트에 10초 타임아웃 설정이다
So why: 서울 열린데이터 API는 공공 API라 응답이 느려지거나 무응답이 오는 경우가 있다. 타임아웃이 없으면 무한정 기다리면서 Tokio 태스크가 블로킹 하게 된다. 30초 수집 주기 안에 응답을 못 받으면 다음 주기가 밀리기 때문에 10초로 끊었다
let delay = calc_delay_seconds(&t.recptn_dt, &now);
let adjusted = a.barvl_dt.parse::<i64>().unwrap_or(0) - delay;
ArrivalInfoResult {
barvl_dt: adjusted.max(0),
...
}
So what: API 응답의 도착 예정 시간에서 데이터 수신 지연 시간을 빼서 보정한다
So why: barvl_dt는 API 서버가 데이터를 생성한 시점 기준의 도착 예정이다 Kafka로 전송하고 Valkey에 저장되고 Flutter가 읽는 시간동안 시간이 흐르게 되는데 보정 없이 그대로 내려줄 경우 이미 지나간 시간이 될수도 있어 recptn_dt와 현재 시각 차이를 빼서 실제 남은 시간을 계산한다.
tokio::select! {
_ = location_interval.tick() => {
produce_train_location(&service, &producer).await;
}
_ = arrival_interval.tick() => {
produce_arrival_info(&service, &producer).await;
}
}
So what: 열차 위치와 도착정보 수집을 하나의 루프에서 동시에 처리한다.
So why: 두 수집을 별도 태스크로 분리하면 각자 독립적으로 실행되지만 관리 포인트가 늘어난다. tokio::select!로 하나의 루프에서 두 Interval을 모두 감시하면 어느 쪽 타이머가 먼저 터져도 즉시 처리하고 하나가 느려져도 다른 쪽 수집을 블로킹하지 않는다
'Project > SSAFY2학기 특화 PJT' 카테고리의 다른 글
| [BE] congestion.rs ( 혼잡도 AI ) (0) | 2026.03.13 |
|---|---|
| [BE] routes.rs ( TMAP API ) (0) | 2026.03.13 |
| [BE] kakao.rs ( KAKAO API ) (0) | 2026.03.13 |
| [BE, AI] 언어 선택 이유 (0) | 2026.03.13 |
| [BE] odsay.rs ( ODSAY API 연동 ) (0) | 2026.03.13 |
