플라워로드 기술 블로그 : http://blog.flowerroad.ai
Notion Link : https://flyingcorp.notion.site/Backend-Flow-4b657c65bef14b2496acf2b41ae9aab9
도입
이전장(플라워로드 시스템 아키텍처 - 도입편)에서 우리 플라워로드 Backend 시스템에서 사용되는 기술들에 대해서 간략하게 알아보았습니다. 여기서는 설명한 기술들을 이용해서 시스템이 어떻게 구성되어 있고, 어떤 플로우로 서비스를 제공하고 있는지 살펴볼 예정입니다.
플라워로드는 크게 사용자앱, 관리자앱, 스쿠터 세가지의 Frontend로 구분할 수 있습니다. Backend 시스템 내에서 각각의 Frontend에 따른 구분은 없고, 기본적으로 Backend의 computing system은 MSA를 따라 Lambda function들의 묶음으로 구성되어 있기 때문에 Frontend로 부터 요청되는 데이터 혹은 command에 따라서 사용되는(호출되는) Lambda function의 묶음이 결정됩니다.
Flow
아래 그림은 플라워로드의 데이터 흐름을 보여주고 있습니다.
화살표의 색깔을 보고 따라가기만 해도 흐름에 대해서 파악 할 수 있기 때문에 크게 설명할 내용은 없을거라 생각이 되기 때문에 각각의 component만 간단하게 소개합니다.
Rest API
AWS API Gateway에서 요청을 받아서 각 요청에 맞는 handler(Lambda)를 호출하는 역활을 합니다. URL에 대한 assign은 AWS Route53으로 구성되어 있고, 요청에 대한 인증은 AWS Cognito와 api key를 API의 특성에 맞게 사용하도록 설정되어 있습니다.
Handler
여기서 말하는 Handler는 모두 Lambda Function을 말합니다. MSA를 기반으로 설계를 했기 때문에 작은 단위의 service로 분류 한 후 Lambda로 구현했습니다. 이렇다보니 기능이 늘어남에 따라 Lambda의 숫자도 많아지고 Lambda의 call횟수도 기하급수적으로 늘어나고 있는걸 볼수 있습니다.
그나마 다행인것은 AWS Lambda의 요금을 보면 Call 횟수에 대한 요금 부담 보다는 상대적으로 Lambda가 실행되는 총 시간(computing time)에 대한 요금이 더 큰편입니다.(이건 저희 서비스의 경우입니다. 너무 작은 단위로 service를 구성하면 call 횟수에 따른 금액이 더 커질수도 있습니다.) 따라서, IO가 필요한 service들은 최대한 빠르게 완료 될수 있도록 구성 및 구현을 하거나 caching, 임시 저장소 등을 이용해서 lambda chaining 방식 혹은 step function 으로 처리해야 합니다.
Redis
여기서 사용된 Redis는 AWS ElastiCache의 Redis를 사용했습니다.
주로 스쿠터의 current data를 caching하는 역활을 하고 있습니다. 예를 들면 현재 좌표, 스쿠터의 주행 상태, 사용자의 점유 상태, 배터리 상태, 잠금 상태 등 앱에서 현재 위치 주변의 스쿠터 정보를 요청했을때 빠르게 데이터를 획득 및 전달 해주는 역활을 맡고 있습니다. 그리고, 아직 적용은 하지 않았지만 pub/sub 기능을 이용해서 스쿠터의 정보에 변경이 있으면 즉각적으로 시스템 혹은 앱에게 알려주는 기능도 적용예정에 있습니다.
사실 초기 설계시 Redis를 사용하게 된 이유는 따로 있었습니다. 당시 AWS aurora가 mysql 5.6 version 까지만 지원을 해서 좌표를 이용한 범위, 거리 검색이 되지 않았기 때문이였습니다. redis의 경우 중심좌표를 기준으로 특정 거리내의 좌표를 query할 수 있기 때문에 이를 이용해서 사용자 중심 반경 특정 범위 내의 스쿠터만 쉽게 조회가 가능했었습니다.
RDS DB
최근 NoSQL DB(mongoDB, dynamoDB등)도 많이 사용되고 있어서 설계 당시 조금 고민을 하기는 했습니다만, 역시 결제 기능이 있어서 transition을 지원하는 RDB의 사용을 선택했습니다.
초기 설계 당시 AWS의 serverless Aurora를 사용하려고 했으나 그 당시 seoul region에는 serverless가 서비스 되고 있지 않던 관계로 일반 서버 형태를 사용하게 되었습니다. mySql 5.6 버전을(당시 사용할 수 있는 최신 버전.....)사용했었고, 초기에는 main과(write 용) read replica 하나를 생성해서 사용했었고, 현재는 data analysis용으로 read replica를 하나더 생성해서 사용하고 있습니다.
전통적인 DB이다 보니 모두들 예상하실만한 데이터는 여기에 다 있습니다.(user info, billing, payment등등)
Tracker DB
TrackerDB는 NoSQL 형태의 dynamoDB를 사용하고 있습니다. 초기에 mongoDB를 고려 했으나 당시에 AWS에서 mongoDB 서비스를 launch하지 않은 때라서 어쩔수 없이 선택한 면도 있습니다.
Tracker DB에는 스쿠터의 모든 좌표 데이터가 저장되고 있습니다. 각 스쿠터 마다 짧게는 10초 길게는 1분에 한번씩 스쿠터의 데이터가 서버로 전송되고 이 데이터들이 모두 저장되고 있습니다. 당연히 스쿠터 대수가 늘어남에따라 저장되는 데이터의 양도 기하급수적으로 늘어나고 있습니다. 따라서, 주기적으로 S3에 Backup을 해야줘야 불필요한 요금지불을 막을 수 있습니다.
S3 스쿠터의 좌표 history data는 주로 data analysis, ML, 사용자 패턴분석등에서 많이 사용되고 있고, 관리자용 앱에서 최근 스쿠터의 이동 내역을 검색 할때에도 사용되고 있습니다.
TCP Server
스쿠터에 장착되어 있는 IoT의 제조업체마다 조금씩 다른 통신 프로토콜을 사용하고 있습니다. 저희가 서비스 중인 스쿠터중 일부의 IoT기기는 TCP socket통신을 이용해서 서버와 접속/연결하고 데이터를 주고 받고 있습니다.
IoT용인 MQTT가 있는데 왠 Socket이냐 라고 생각하실 수 있는데 초기 IoT의 HW에 장착된 LTE칩은 칩셋 제조사가 제공해주는 기능이 그리 많지 않아서 MQTT를 지원할수 없는 경우가 많았습니다.(당연히 비싼칩은 다 되지만요...ㅠㅠ)
server는 NodeJs로 구현되어 있습니다. 컨셉을 MQTT의 message broker와 동일하게 잡았기 때문에 client로 부터 데이터를 받으면 데이터를 처리하는 lambda를 invoke하는 일만 해서 데이터의 직접적인 처리는 하지 않고 있습니다.
Event Scheduler
설계 초기에는 크게 생각하지 않았었는데 서비스를 진행하면서 주기적으로 실행 해야할 일들이 많아져서 추가되었고 지금도 계속해서 늘어나고 있네요... 예를 들어서, 일일 서비스 실적을 계산해서 전사에 메일로 보낸다거나, 사용자의 쿠폰중 만료날짜가 가까워지는 것들에 대해서 push를 보내준다거나 사용자가 사용종료를 하지 않고 스쿠터를 세워 둔경우가 있는지에 대한 체크 등등 주기적으로 해야할일이 많아지고 있습니다.
event scheduler는 AWS의 Event Rules를 이용해서 여러 schedule를 등록해서 처리하고 있습니다. 등록할때 crontab형태로 period를 설정할 수 있어서 어려움 없이 설정할수 있었습니다.
모든 event들은 해당 event에 맞는 Lambda를 invoke하게해서 실제 처리는 dedicated되어 있는 Lambda가 맡고 있습니다.
(2022.02.22 일 현재는 AWS SQS 와 함께 이용하고 있습니다.)
Flow - 스쿠터 검색
위의 flow 그림에서 파란색 화살표의 procedure입니다. 사용자가 앱을 켰을때 혹은 새로고침 버튼을 눌렀을때에 REST API를 이용해서 현재 위치 주변의 스쿠터 검색을 요청합니다. API Gateway로 들어온 요청은 연결되어 있는 스쿠터 검색 handler를 invoke하게 되고 해당 lambda에서 Redis에 위치+거리 기반의 스쿠터 좌표 및 현재 스쿠터 정보 data를 요청/조회 합니다. 조회가 완료 되면 앱에서 사용하기 쉬운 형태의 데이터로 변환 해서 앱으로 전달해줍니다.
당연한 말이겠지만 빠르게 처리되어야 합니다. 사용자가 앱을 실행하거나 맵의 중앙 위치를 옮겼을때 빠르게 반응해서 표출되어야 하기 때문에 최대한 빠르게 처리 될 수 있도록 구성되어 있습니다.
여기서 문제점이 하나 발생합니다. REST API형태로 스쿠터를 검색하게 되니 사용자가 새로고침을 누르기 전까지 혹은 맵의 위치를 움직이기 전까지 스쿠터데이터가 업데이트 되지 않는다는 점입니다. 이는 사용자가 최초 스쿠터 위치를 확인하고 걸어가는동안 해당 스쿠터를 누군가가 타고 가버렸을때 사용자는 알수 없다는 문제가 발생합니다.(실제로 이 문제 때문에 CS 콜이 오기도 합니다.)
그래서, 저희는 스쿠터의 검색을 REST API 호출 형태가 아닌 접속 유지를 통해서 pub/sub의 형태로 client에게 스쿠터의 상태변화를 실시간으로 업데이트 해줄 수 있는 architecture적용을 진행중에 있습니다.(WebSocket and/or gRPC 이용)
Flow - 사용 시작
REST API를 이용해서 사용시작 요청이 들어오면 해당 역활을 맡은 Lambda가 실행 됩니다. 이 Lambda는 해당 사용자 및 스쿠터가 이용가능한 상태인지 확인하고, 문제가 없다면 스쿠터에게 unlock명령을 전달합니다. 이때 명령전달은 스쿠터의 IoT종류에 따라 나뉘게 되는데, TCP server에 접속해서 send를 하거나 스쿠터에게 데이터를 전달하는 Lambda를 invoke하게 됩니다.
이렇게 unlock이 문제 없이 완료되면(에러가 발생하면 앱에게 실패했다는 응답을 보내고 끝납니다.) 사용 시작 데이터 처리 및 저장을 위해서 해당 기능을 담당하는 lambda를 invoke하고, 그 lambda는 RDS에 사용자의 라이딩 시작, 상태, 시간등을 저장하고 redis에 cache된 데이터도 최신으로 업데이트합니다.
Flow - 사용 종료
사용 시작 요청과 거의 동일한 flow로 흘러갑니다. API Gateway로부터 사용 종료를 담당하는 Lambda가 invoke 되고, 스쿠터의 잠금을 담당하는 Lambda와 결제 및 결제 데이터를 업데이트 하는 Lambda가 invoke되어 처리하게 되어 있습니다.
단하나 다른점은 위치를 보정하는 lambda가 invoke되어 실행된다는 점입니다. 도심지 내에서의 IoT GPS데이터는 생각보다 위치 오차율이 큽니다. 작게는 1~2m에서 크게는 50m이상 차이가 발생할 때도 있습니다. 따라서, 이를 해결하기 위해서 여러 좌표데이터, 요소, 환경 등의 데이터를 취합해서 분석 후 오차를 보정하고 있습니다. 물론 이는 아직 완전하지 않고 지속적으로 ML, data analysis등을 통해서 개선 진행중에 있습니다.
마치며...
이번 포스팅에서는 공유 스쿠터 서비스인 플라워로드의 대략적인 시스템 Flow에 대해서 살펴봤습니다. 전체적으로 작성하고 보니 특별할것도 없고 복잡할것도 없어보이네요. 다음 포스팅에서는 플라워로드를 위해서 사용되고 있는 AWS service들이 어떻게 구성되어 있고, 어떻게 사용되고 있는지에 대해서 살펴볼 예정입니다.
'개발 > backend' 카테고리의 다른 글
[Backend] 쿠버네티스(K8S)를 시작해보자(1) - 이론편 (0) | 2022.03.06 |
---|---|
[AWS] AWS RDS - MySQL vs Aurora (0) | 2022.02.27 |
[Backend] 공유 스쿠터 서비스 - 플라워로드 시스템 아키텍처 : AWS Architecture (0) | 2022.02.24 |
[Backend] 공유 스쿠터 서비스 - 플라워로드 시스템 아키텍처 사용 기술들 (0) | 2022.02.23 |
[Backend] AWS WebSocket 적용하기 (Feat. APIGateway, Lambda) (0) | 2022.01.28 |