#[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

+ Recent posts