Skip to content
Role
기획 · 디자인 · 개발 · 운영 (1인)
Team
1인
Period
2026.05 – 2026.09
Tools
Claude Code, Apps in Toss SDK, TDS, Supabase, Vercel

지하철 칸별 혼잡도 미니앱

칸칸

칸칸 라이브 화면 – 노원에서 강남까지 환승 두 구간의 칸별 혼잡도, 시간대를 바꾸면 여유·보통·혼잡이 갈린다

요약

경로는 이미 안다. 남은 결정은 어느 칸에 타는가다. 출발역과 도착역만 넣으면 시간대별로 덜 붐비는 칸과 빠른환승 칸을 한 화면에 보여주는 토스 미니앱이다.

통과 · 출시
앱인토스 심사

토스 안에서 도는 미니앱으로 배포

77건
손으로 고친 빠른환승 오류

실측 · 두 출처 교차검증

456건
앱에 번들한 검증된 데이터

산출물 · 틀린 실시간 값을 내보내지 않기 위해

개인 프로젝트 – 기획 · UX · 디자인 · 개발 · 데이터 · 심사 대응 · 운영 (Claude Code와 1인)

개요

경로는 이미 안다. 남은 결정은 어느 칸에 타는가다. 지도 앱은 최단 경로까지만 알려준다. 앉아 갈 수 있는 칸은 사용자가 매번 감으로 고른다. 매일 지하철을 타며 느낀 불편에서 시작했다.

칸칸은 토스 안에서 도는 미니앱(앱인토스)이다. 기획, 디자인, 개발, 심사 대응, 운영을 1인으로 맡았다. 심사를 통과해 출시했다.

라이브: kankan-eta.vercel.app

문제

문제는 한 문장이다. 어느 칸에 타야 앉아 가는가.

  • 자리는 매번 감으로 찾는다 – 매일 지하철을 타는 사람도 어디에 서야 앉는지는 감으로 정한다.
  • 지도 앱은 칸을 보지 않는다 – 네이버·카카오 지도는 최단·최적 경로만 보여준다. 칸별 혼잡도는 없다.
  • 타깃 – 출퇴근·등하교 때 자리를 원하지만 정보가 없어 오늘도 서서 가는 지하철 이용자.

핵심 결정

  1. 01열차가 오기 전에 답을 준다. 입력 한 번, 결과 한 번

    탑승 직전 몇 초 안에 판단하는 사용자에게 탭, 설정, 온보딩은 모두 늦다.

    →출발역과 도착역만 넣으면 바로 결과가 나오는 단일 화면 흐름이다. 역 검색은 초성으로도 된다.

  2. 02환승까지 한 화면에서 끝낸다

    앉아 가는 칸과 환승하기 좋은 칸이 다르면 사용자는 결국 다른 앱을 다시 연다.

    →구간마다 칸 혼잡도를 보여준다. 환승 구간에는 빠른환승 칸 번호를 함께 적었다.

  3. 03숫자보다 색으로 읽게 한다

    서서 기다리는 사람은 글자를 읽을 시간이 없다. 화면을 의심할 틈도 없다.

    →혼잡도를 초록 · 노랑 · 빨강으로 구분했다. 퍼센트는 보조로 뒀다. 추천 칸에는 표시를 붙였다.

  4. 04공공데이터를 그대로 믿지 않는다

    빠른환승 칸 정보를 기존 길찾기 앱과 역별로 맞춰 보자 누락과 오기가 나왔다. 잘못된 칸 번호는 곧 잘못된 행동이 된다.

    →국토교통부 데이터와 서울교통공사 API를 교차검증했다. 어긋난 77건은 손으로 확인해 고쳤다(실측).

  5. 05틀린 실시간 값보다 검증된 정적 데이터를 택했다

    라이브 API가 불안정한 구간이 있었다. 최신값을 보여주는 것보다 틀린 칸 번호를 내보내지 않는 쪽이 중요했다.

    →검증을 마친 빠른환승 456건을 앱에 정적 데이터로 번들했다(산출물). 실시간 호출은 여기에 쓰지 않는다.

과정

제약 속 설계

플랫폼, 인프라, API의 제약을 기획 초기에 파악했다. 제약마다 디자인과 개발 결정에 반영했다.

  • 앱인토스 배포 정책 – 진입 즉시 팝업 금지, 외부 앱 유도 금지, 뒤로가기 차단 금지를 모두 지켰다. 뒤로가기 버튼을 없애고 네이티브 제스처에 맡겼다. 이 조건을 맞춘 뒤 심사를 통과했다.
  • WebView 환경 – 앱인토스 SDK와 웹 프레임워크 가이드를 따랐다. 네이티브 코드 없이 WebView 안에서 끝냈다.
  • API 무료 한도 – 혼잡도는 자주 바뀌지 않는다. 배치로 모아 Supabase에 캐싱하고 실시간 호출을 줄였다. 하루 1,000건 한도 안에서 운영한다. 지속 운영 비용은 0원이다.

디자인

토스 디자인 시스템(TDS) 컴포넌트를 그대로 썼다. 토스 앱 안에서 이질감 없이 보인다.

쓴 TDS 컴포넌트
  • SearchBar – 역 검색 입력.
  • Badge – 혼잡도 표시(색상 코딩).
  • CTAButton – "칸 추천받기", 해야 할 행동 하나.
  • NavigationBar – 브랜드 로고와 가운데 정렬 타이틀.

데이터

데이터 출처와 처리 방식
  • 칸별 혼잡도 · 하차 비율 – 퍼즐 API(지오비전 SK), 30분 단위, 1~8호선. 배치로 수집해 Supabase에 캐싱하고 시간대별로 조회한다.
  • 빠른환승 칸 번호 – 국토교통부 CSV와 서울교통공사 API. 두 출처를 교차검증해 오류 77건을 고치고 456건을 정적 데이터로 번들했다.
  • 유지보수 – 혼잡도는 배치 스크립트로 Supabase를 갱신한다. 역 정보가 바뀌면 빠른환승 데이터를 다시 검증해 교체한다. API 한도는 Vercel 로그로 확인한다.

결과

출시한 것이 결과다. 심사를 통과해 토스 안에서 돌고 있다. 사용 지표는 아직 재지 않았다.

통과 · 출시
앱인토스 심사
77건
손으로 고친 데이터 오류 (실측)
0원
지속 운영 비용

사람이 개입하는 지점

  • 자동 – 공공데이터를 두 출처로 교차검증해 미리 캐시에 쌓는다.
  • 사람 – 교차검증에서 어긋난 77건은 사람이 확인해 고쳤다. 라이브 API가 불안정한 구간은 검증된 456건을 정적 데이터로 번들했다. 틀린 값을 실시간으로 내보내지 않는다.

회고

맞았던 것

  • 같은 데이터도 맥락이 바뀌면 다른 서비스가 된다. 혼잡도 데이터는 이미 있었다. 탑승 직전이라는 맥락에 맞추자 전혀 다른 가치가 됐다.
  • 틀린 값을 내보내지 않기로 한 결정. 최신값 대신 검증된 456건을 택했다. 커버리지는 1~8호선에서 멈췄지만 화면에 잘못된 칸 번호는 올리지 않는다.
  • 비용을 없앤 것도 디자인 결정이었다. 혼자 만든 서비스를 오래 열어 두려면 운영비가 0이어야 했다.

틀렸던 것

  • 공공데이터가 서로 맞을 거라고 봤다. 두 출처를 맞춰 보니 77건이 어긋났다. 한 출처만 믿었다면 틀린 칸 번호가 그대로 나갔을 것이다.
  • 실시간 값이 항상 낫다고 봤다. 라이브 API의 빠른환승 정보는 실제와 달랐다. 최신인지보다 맞는지가 먼저였다.

다음에 확인할 것

  • 추천이 맞았는지 재지 않았다. 추천한 칸에서 실제로 앉았는지 확인할 방법이 아직 없다.
  • 혼잡도는 30분 단위 시간대 값이다. 그날의 실제 혼잡도와 얼마나 벌어지는지 모른다.
  • 호선 확대 – 1~8호선 밖에서도 같은 품질의 칸별 데이터가 나오는지 확인해야 한다. 9호선과 수도권 광역철도(GTX)가 다음 후보다.
  • 알림 – 출발 전 푸시로 칸을 알려주는 방식이 탑승 직전 판단보다 나은지 아직 모른다.