개요
따릉이
는 서울시에서 운영하는 공유자전거 서비스입니다. 따릉이의 이용자 수는 매년 빠르게 증가하고 있고 2021년 기준 가입자 330만명, 이용건수 3천 300만건입니다. 이는 서울시 인구 1/3이에 해당하며 이러한 증가 추세에 맞춰 서울시는 따릉이 사업을 확대하고 있습니다. 또한 따릉이는 친환경 이동수단으로 대중교통과의 환승을 도입하는 등 사용이 장려되고있는 사업입니다. 하지만 이러한 정책적 지원과 별개로 따릉이 사용의 편의를 위한 서비스는 부족하다고 생각됩니다.
프로따릉러
는 따릉이 전용 경로 추천 시스템입니다. 따릉이 대여소 정보를 제공하며 경로 탐색 시 출발지와 목적지 주변 대여소를 기반으로 경로를 추천해줍니다.
서비스는 이곳에서 이용할 수 있습니다.
문제
현재 사용자가 따릉이를 이용할 때 경로 탐색 과정을 살펴보면 다음과 같습니다.
- 따릉이 앱에서 출발지 근처 대여소를 찾고 잔여 자전거가 있는 대여소를 선택한다.
- 지도 앱에서 목적지 근처 대여소를 검색하고 반납할 대여소를 선택한다.
- 대여소간 자전거 이동 경로를 검색한다.
위 과정은 우리가 대중교통이나 자동차 경로를 탐색할 때와는 차이가 있습니다. 두개의 앱을 사용자가 직접 조합해서 사용해야 하며 어떤 대여소를 용할지 사용자가 육안으로 보고 선택해야 합니다.
이런 불편이 발생하는 이유는 따릉이 앱은 경로탐색을 지원하지 않고, 지도 앱은 대여소 정보를 제공하지 않기 때문입니다. 따릉이도 대중교통과 같이 출발지와 목적지의 입력만으로 경로 탐색을 할 수 있으면 좋겠다는 생각에 이 프로젝트를 시작하게 되었습니다.
- 대여소의 자전거 잔여량 확인과 경로 탐색을 모두 제공하는 서비스의 부재
- 경로탐색시 출발지와 목적지의 대여소를 직접 선택해야하는 불편함
기능
위와 같은 문제를 해결하고자 프로따릉러
는 다음과 같은 기능을 제공합니다.
- 기본 정보
- 지도: 클릭 혹은 터치로 지역 이동, 선택이 가능합니다.
- 현재 위치: 실시간위치를 추적하여 추천 경로와 현재 위치의 비교가 가능합니다.
- 위치 검색: 키워드로 원하는 위치를 검색할 수 있습니다. 따릉이는 서울시 사업으로 범위가 한정되어 있기 때문에 검색 범위는 서울로 제한했습니다.
- 대여소 정보
- 주변 대여소 검색: 클릭/터치 혹은 검색으로 지역을 선택하면 근처 대여소를 확인 할 수 있습니다. 대여소를 출발지 혹은 도착지로 선택할 수 있습니다.
- 잔여 자전거 수 확인: 대여소의 실시간 자전거 대여현황을 확인할 수 있습니다.
- 경로 추천
- 경로 탐색: 출발지와 목적지만으로 경로 탐색이 가능합니다. 자전거 도로 우선 경로를 제공합니다. 예상 소요시간과 거리, 네비게이션 메세지를 제공합니다.
제작
- 서비스 구조
- 클라이언트는 단일 페이지에서 상호작용을 통해 동적으로 데이터를 보여주기 때문에 싱글 페이지 어플리케이션이 가장 적합하다고 생각하여 Reactjs를 사용했습니다.
- NginX를 리버스 프록시 서버로 사용해 서버 앞단에서 빌드된 페이지 호스팅하며 API 초당 요청 비율을 제한합니다.
- 메인 서버는 서비스 로직에 집중하기 위해 클라이언트와 같은 언어인 Typescript를 사용하고 자유도가 높은 Express에 비해 구조가 짜여있는 Nestjs로 제작했습니다.
- 데이터베이스는 공간 데이터의 처리와 연산을 쉽게 하기 위해 PostgreSQL과 PostGIS 플러그인을 사용했습니다.
- 라우팅 엔진(경로 탐색 엔진)은 오픈소스 Valhalla 엔진을 사용했습니다.
- AWS EC2에 배포했습니다.
- 데이터
서비스에 사용되는 데이터는 다음과 같이 크게 3가지로 구분할 수 있습니다.
- 대여소 관련 데이터
- 경로 탐색에 필요한 데이터
- 위치 키워드 검색에 필요한 데이터
대여소 관련 데이터는 서울시 공공데이터 포털에서 제공하는 따릉이 대여소 마스터 정보와 공공자전거 실시간 대여정보를 조합하여 사용했습니다.
실시간 대여정보의 경우 API 형태로 제공되며 인덱스 범위로 요청할 수 있습니다. 이때 각 대여소의 인덱스가 무엇인지 알 수 없고 인덱스 범위가 최대 1000으로 제한되어 있습니다. 따라서 하나의 대여소의 실시간 대여정보를 가져오기 위해서는 2700개 정도의 대여소 정보를 3번의 요청으로 나누어 가져온 뒤 원하는 대여소를 찾아야 합니다.
이를 최적화 하기 위해 서버에서 캐시를 사용하고 캐시에서 만료된 인덱스들에 대해서 가장 짧은 구간을 계산해 요청합니다. 예를 들어 짧은 시간(캐시 TTL)안에 두번의 요청이 들어온 상황을 가정해봅시다. 첫번째 요청에서 인덱스 1, 10, 2500, 2600, 3700번 대여소 정보가 요청면 캐시를 확인하고 검색해야 하는 모든 인덱스를 포함하는 길이 1000이하의 가장 짧은 구간들을 계산합니다. 이 경우에는 (1~10), (2500~2600), (3700~3700) 세개의 구간이 생성되고 공공자전거 실시간 대여정보 API에 계산된 구간을 요청합니다. 이어서 두번째 요청에서 인덱스 5, 150, 2550, 2800, 3700번 대여소 정보가 요청되면 5, 2550, 3700번은 캐시에 살아있으니 제외되고 150, 2800번에 대해서 구간을 생성합니다. 따라서 두번째 요청에서는 (150~150), (2800~2800) 두개의 구간이 생성되고 요청됩니다.
- 경로 탐색 엔진(Routing Engine)
가장 중요한 기능인 경로탐색은 OpenStreetMap(이하 OSM)기반 오픈소스 경로 탐색 엔진 Valhalla 엔진을 사용했습니다. API 형태로 경로 탐색을 제공하는 곳이 많지만 대부분 사용량에 제한이 있고 자전거 우선 경로를 지원하지 않습니다. 따라서 오픈소스 경로 탐색 엔진을 직접 사용하기로 했습니다.
서비스 지역이 서울이기 때문에 OSM에서 제공하는 대한민국 OSM 데이터에서 서울 부분만 추출하여 사용했습니다.
주황색으로 보이는 선은 자전거 전용 도로를 나타냅니다. 자전거 경로 우선 탐색은 자전거 전용도로가 가중치를 부여받아 경로가 계산됩니다.
소스코드와 프로덕션 링크
- 클리이언트 소스코드: https://github.com/thecloer/pro-bike-seoul-client
- 서버 소스코드: https://github.com/thecloer/pro-bike-seoul-server
- 배포된 프로젝트: https://probikeseoul.thecloer.com
회고
프로젝트를 마무리 한지 4개월 정도가 지났습니다. 블로그를 이전하며 글을 다시 작성하며 회상하니 재미도 있었고 아쉬움도 많이 남습니다. 첫 프로젝트는 서비스 기획, 디자인, 데이터 수집 및 전처리, 개발, 배포 등 처음부터 끝까지 모든 과정을 혼자서 해보고 싶다는 생각에 개인 프로젝트로 진행했습니다.
데이터 수집하고 전처리하는 과정에서 예상보다 훨씬 많은 시간이 소모됐고 오픈소스와 외부 API를 사용하다 보니 공식문서를 읽고 오류를 해결하는데도 시간이 많이 소모됐습니다. 예상치 못한 시간 소모와 혼자서 할게 많다 보니 시간에 쫓겨 "더 잘할 수 있었는데"라는 아쉬움이 큰 것 같습니다.
이 프로젝트를 진행하며 서비스의 한 사이클을 혼자서 진행한 경험이 다음 프로젝트에 큰 도움이 될 것 같습니다. 프로젝트 전에는 무언가를 제작 할 때 부분에서 전체로 구현하던 경향이 있었습니다. 주요 기능을 구현하고 기능을 사용하는 인터페이스 혹은 부가 기능을 붙이는 식으로 사이즈를 키워나갔습니다. 이번 프로젝트를 진행하며 기획 단계에서 세부 기능들의 명세를 정하는 것과 각 기능의 구현 단계에서 서버와 클라이언트 사이 인터페이스(REST API) 명세를 정하는 것에 중요함을 느꼈습니다. 다음 프로젝트에서는 전체에서 부분으로 골격을 짜고 안을 체우는 식으로 진행해 보려 합니다.