손호영
Lead Engineer2023.07 – 현재

RailNetwork

6,375명의 활성 사용자와 12M+ 이벤트를 처리하는 서울 지하철 실시간 추적 서비스 TrainAccAPI 백엔드를 설계·구축했습니다. (2025.11 – 2026.02 기준)

1,400+
DAU
12M+
이벤트
20
노선
2,000+
추적 열차
15s
업데이트
99.5%
가용성
RailNetwork 아키텍처 다이어그램
시스템 아키텍처 — 클릭하면 원본으로 열립니다

01설계 결정 — 왜 이렇게 만들었나

불안정한 공공 API 위에서 서비스 품질을 지키는 구조를 골랐습니다.

폴백을 어댑터 계층에서 같은 포맷으로 정규화한 이유

SMSS와 열린데이터는 응답 스키마·필드 의미가 다릅니다. 폴백 분기를 서비스 로직에 두면 '어느 소스에서 왔는지'가 비즈니스 코드 전체로 번집니다. 그래서 두 소스를 각각 래퍼로 감싸 동일한 ttcVOList 포맷으로 정규화하고, 상위 로직은 소스의 존재 자체를 모르게 했습니다. 포트/어댑터를 교과서로 배우기 전에 필요해서 먼저 도달한 구조인데, 덕분에 폴백 전환이 무공수 스왑이 됩니다.

부족한 데이터는 구조가 아니라 사용자로 메꿨다

공공 API는 열차 편성(어떤 차량이 몇 량인지) 정보를 주지 않습니다. 크롤링으로 풀 수 없는 데이터라, 사용자 제보 → 투표 검증 → 반영의 크라우드소싱 루프를 설계해 데이터 자체를 사용자와 함께 만들었습니다. 모든 문제를 코드로 풀지 않는 것도 설계 결정이라고 생각합니다.

02시스템 설계

공공 API의 불안정성을 서비스 품질로 흡수하는 데 집중했습니다.

아키텍처

열차위치조회·편성정보·운행계획·크라우드소싱 4개 서비스 도메인으로 설계한 백엔드입니다. SMSS 실패 시 열린데이터 API로 자동 폴백하는 이중 API 시스템을 구축하고, Express.js REST API와 gRPC 서버를 병행 운영합니다. Redis 캐싱과 MongoDB로 데이터 지속성을 확보하며, Firebase Analytics로 지역별 사용자 분포와 트래픽 패턴을 분석합니다.

성능 최적화

Promise.all 기반 병렬 API 호출로 전체 노선 업데이트 시간을 75% 단축하고, 압축 미들웨어로 네트워크 트래픽을 40% 절감했습니다. Redis 캐싱으로 평균 응답시간 150ms 이하, 99.5% 가용성을 유지하며 일 평균 1,400+ DAU를 처리합니다.

보안 및 안정성

HMAC-SHA256 해시와 Nonce를 활용한 디지털 서명 검증 시스템을 구축하고, JWT 인증과 Rate Limiting, IP 지역 제한으로 보안을 강화했습니다. Winston 구조화 로깅과 에러 처리로 24/7 안정 운영을 달성했습니다.

03사용자 분석

데이터로 서비스 방향을 결정했습니다.

지역 분포와 리텐션

Firebase Analytics 기반 분석 결과 서울 68%·경기 22%·기타 10%로 수도권 사용자가 90%를 차지합니다. 90일간 12M+ 이벤트, 일 평균 1,400+ DAU를 유지하며, 크라우드소싱 편성 투표로 사용자 참여를 유도했습니다.

풀어낸 케이스

마주친 문제 · 고민하고 적용한 접근 · 실제 결과를 한 세트로 묶어 정리했습니다.

01CASE
data-qualitydomain
문제

2호선 순환선은 지연이 발생하면 열번이 바뀌어, 같은 열차를 계속 추적할 수 없었습니다.

고민·접근

SMSS 응답을 열린데이터 응답과 병합·중복 제거하고, trainNo 앞자리를 노선 규칙에 맞게 강제 보정하는 로직을 만들었습니다.

결과

지연 상황에서도 편성 추적 연속성이 유지됩니다.

추적 연속성 확보
02CASE
resiliencefallback
문제

단일 공공 API(SMSS)가 불안정해 장애 시 서비스 전체가 멈췄습니다.

고민·접근

서울시 열린데이터 광장 API를 동일 포맷(ttcVOList)으로 감싸 자동 폴백 계층을 만들고, 노선별 특수 파라미터를 객체로 분리했습니다.

결과

단일 API 장애에도 서비스가 지속됩니다.

가용성 99.5%
03CASE
performance
문제

20개 노선을 순차 폴링하면 전체 갱신 주기가 목표(15초)를 넘었습니다.

고민·접근

Promise.all 기반 병렬 호출로 재구성하고 응답 압축을 적용했습니다.

결과

전체 노선 업데이트 시간 75% 단축, 트래픽 40% 절감.

-75% 갱신 시간

프로젝트 정보

역할Lead Engineer
기간2023.07 – 현재
상태Production

기술 스택

Node.js 18+Express.jsgRPCMongoDBRedisJWTHMAC-SHA256FlutterWinstonFirebase AnalyticsDockerSwagger

주요 성과

  • 20개 노선, 2,000여 대 열차 실시간 추적
  • 6,375명 활성 사용자, 12M+ 이벤트 처리 (90일 기준)
  • 일 평균 1,400+ DAU · 99.5% 가용성
  • 15초 간격 실시간 업데이트
  • Android/iOS 크로스 플랫폼 서비스 제공 (Flutter 기반)