Skip to content
Role
Strategy · design · build · operations (solo)
Team
Solo
Period
2026.05 – 2026.09
Tools
Claude Code, Apps in Toss SDK, TDS, Supabase, Vercel

A subway car crowding mini-app

Kankan

Summary

“A seat, even at rush hour.” Enter where you get on and off, and one screen shows how crowded each car is at that hour, and which car is closest to your transfer.

77
quick-transfer errors fixed

cross-checked across two sources

456
quick-transfer records bundled
₩0
running cost

batch caching within free limits

Personal project – strategy, UX, design, build, data, review and operations (solo, with Claude Code)

1 / 4
Kankan in use (0:45) – search and results recorded from the live app; the per-car crowding screens come from earlier captures, marked as such, because the data batch is currently down.

Overview

It started with my own commute. Map apps focus on the route, but for someone who already knows the route, what matters is which car to board to get a seat. Kankan launched as a mini-app inside Toss (Apps in Toss).

Problem

  • Finding a seat is guesswork – daily riders rely on instinct to decide where to stand.
  • Map apps don't look at cars – Naver and Kakao Maps show the shortest or best route, not crowding by car.
  • Who it's for – commuters and students who want a seat but have no information, and stand again today.

Key decisions

  1. 01Answer before the train arrives – one input, one result

    For someone deciding seconds before boarding, tabs, settings and onboarding all come too late.

    A single-screen flow: enter two stations and the result appears. Station search works with Korean initial consonants.

  2. 02Read it by colour, not numbers

    Someone waiting on a platform has no time to read, and no time to doubt the screen.

    Crowding is coded green, yellow and red, with the percentage secondary. The recommended car is marked.

  3. 03Finish the transfer on the same screen

    If the car with a seat and the car nearest the transfer differ, people end up opening another app.

    Each leg shows crowding by car, and the transfer step shows the quick-transfer car number.

  4. 04Don't trust public data as it is

    Checking quick-transfer data station by station against an existing route app turned up missing and wrong entries. A wrong car number becomes a wrong move.

    Cross-checked the Ministry of Land data against the Seoul Metro API, fixed 77 entries and bundled 456 as static data.

Designing within constraints

I mapped the platform, infrastructure and API limits early, and let each one shape a design or build decision.

  • Apps in Toss policy – no pop-up on entry, no pushing users to outside apps, no blocking the back action. The back button was removed in favour of the native gesture.
  • WebView – built to the Apps in Toss SDK and web framework guide, entirely inside a WebView with no native code.
  • Free API limits – crowding doesn't change much, so it is collected in batches and cached in Supabase, keeping live calls within the 1,000-a-day limit.

Design

Toss Design System (TDS) components are used as they are, so the app feels native inside Toss.

  • SearchBar – station search.
  • Badge – crowding, colour-coded.
  • CTAButton – "Recommend a car", the one action.
  • NavigationBar – logo and centred title.

Data

Sources and processing
  • Crowding and alighting ratio by car – Puzzle API (Geovision SK), in 30-minute steps, lines 1–8. Collected in batches, cached in Supabase and read by hour.
  • Quick-transfer car numbers – Ministry of Land CSV and the Seoul Metro API. Cross-checked, 77 errors fixed, 456 records bundled as static data.
  • Maintenance – a batch script refreshes crowding in Supabase; when station data changes, quick-transfer data is re-checked and replaced. API usage is watched in Vercel logs.

Results

Passed · live
Apps in Toss review
Solo
Strategy to operations
₩0
Running cost

Next

  • More lines – from lines 1–8 to line 9 and the metropolitan express lines (GTX).
  • Alerts – before a set departure time, a push with the car most likely to have a seat.

Where a person steps in

  • By itself – public data is cross-checked between two sources and baked into a cache.
  • By hand – the 77 places where the two disagreed were checked and corrected by a person, and where the live API is unreliable, 456 verified records ship as static data so the app never shows a wrong number in real time.

Reflection

  • The same data becomes a different service in a different context. The crowding data already existed; fitted to the moment before boarding, it became something else.
  • Removing the cost was a design decision too. To keep a solo-built service open for long, it had to run at zero.
  • Data, structure and interface are one chain. Owning every step made it visible how a choice in one immediately changes the others.