화상 응급 신고와 태깅을 통한 타인 신고 서비스를 제공하는 Web/ Mobile App
WebRTC와 WebSocket을 사용하여 신고자와 소방 상황실 간의 실시간 화상통화, 채팅 기능을 제공하고
앱을 통해 사용자의 의료 정보를 사전에 등록해 1초라도 빠르게 신고 절차가 이뤄질 수 있도록 하는 서비스.
nfc 태깅을 통해 타 사용자를 대신해서 신고하고, 소방 상황실에서는 태깅 id를 통해 의료 정보를 제공받을 수 있습니다.
1학기 때 친했던 백엔드 친구와 프론트엔드 친구 세명이서 팀을 꾸리고 비전공자 세분을 모셔왔다. 팀원 중 119 신고에 대해 관심이 있는 분이 계셨고 다들 사회 공헌적인 주제를 희망해서 주제 자체는 첫날에 나왔지만
어떤 점에서 우리 서비스가 차별점이 있을까 고민을 많이 하여 E-gen을 통한 응급실 현황 연결, nfc 태깅을 통한 타인 대리 신고 기능을 생각할 수 있었다.
어찌저찌 mvp를 정하니 프엔 전공 친구가 싸탈을 해버려 프로젝트 볼륨이 애매하게 되어 디자인적인 부분은 간소화하고 mvp에 집중하는 것으로 기획을 끝낼 수 있었다.
기획 회의만 이주 꽉 채워하면서 사용자리서치, 소방 대원과 인터뷰, 실제 소방서에서 쓰는 포맷을 참고하여 서비스화를 한다는 가정하에 프로젝트의 완성도를 높이고자 했다.
신고 서비스를 구상하고 모바일 서비스가 확정되면서 기획 초기에는 PWA로 배포하기로 기획했다.
하지만 테스트 결과 PWA에서는 보안적인 문제로 기기의 화면이 켜져 있어야 nfc 태깅이 가능하다는 것을 알게되고, 사실상 억지스러운 유저 플로우가 되버려 기능을 넣을지 말지 고민을 하게됐다..ㅜ
하지만 기존의 서비스와 차별점을 줄 수 있는 프로젝트의 핵심 기능 중 하나였기 때문에 nfc 태깅이 자유로운 안드로이드 개발을 차선책으로 생각할 수 밖에 없었다.
다행히 팀원이 웹 뷰를 제안하여 안드로이드 웹뷰로 웹 서비스를 띄우기로 결정했다!
예상치 못한 백엔드 TURN 서버 문제로 구현한 프론트엔드 socket 코드를 openvidu로 바꿔야했다.
아까운 내 소켓 코드.. 백엔드가 약간 미웠지만 이것도 배움에 일부라고 생각하고 openvidu.js 코드로 바꾸게 되었다.
주변에서 openvidu를 나중에 올리면 충돌나서 설정해줄게 많다고 겁을 줘서 걱정을 많이 했는데 인프라 팀원이 너무 잘해줘서 큰 변경사항 없이 배포를 성공할 수 있었다.
초기 WebRTC 구조
Node.js로 구현한 시그널링 서버를 통해 P2P방식으로 연결했다.
노마드 코더의 Zoom 클론코딩을 참고해서 기초 코드를 작성하고 우리 프로젝트에 맞는 이벤트를 작성했다.
javascript 코드라 백엔드에서 하기 어렵다는 의견이 나와 혼자 개발을 진행했고,
시그널링 서버 구현, 프론트엔드 연결을 마치고 구글에서 제공하는 무료 STUN 서버를 사용해 동작함을 확인했다.
신고 webRTC 로직 | |
---|---|
openvidu 세션관리
오픈비두를 사용하여 세션을 접속하고 연결을 끊는 과정에서 간헐적으로 딜레이가 생겨 세션이 중복되거나 모바일 사용자끼리 세션이 열리는 버그가 있었다.
시그널링 서버로 구현했을 때는 세션 큐를 구현해서 타 신고자가 들어가지 않게 하려했는데 오픈비두에서는 자체적으로 세션을 할당해 주기 때문에 특정 세션 id를 부여해서 버그를 방지할 수 박에 없었다.
그렇기 때문에 추가적으로 백 서버에 요청을 보내서 현재 입장 가능한 세션 id를 get하고 그 id로 openvidu 세션에 연결을 시도하는 로직이 추가되었고 상황실 로직에서도 백 서버와 불필요한 연결이 들어갈 수 밖에 없었다.
세션 연결이 실패하면 다시 119대원과 연결하는 요청을 해야했다.
구글링 결과 axios-retry 라이브러리를 통해 반복적인 rest Api 호출이 가능하다는 것을 알게됐고 요청이 실패했을 경우 1초 간격으로 10번 정도 다시 요청하고 그래도 실패했을 경우 전화 신고를 유도하는 모달을 띄우는 것으로 기능을 구현했다.
이전에는 Axios Interceptors를 통해 response의 Http status가 200이 아닐 때, 더 들어가 401인 경우 access Tocken이 만료된 것으로 생각하여 재발급 api를 요청하고 거기서 실패하면 refresh Tocken이 만료된 것으로 처리해 로그인을 다시 하도록 리다이렉트시켰다.
더 구글링 해보니 Axios 팀에서도 Retry를 작성할 때는 interceptor를 이용하라고 한다. 또한 재시도 전략이라는 것도 있는데 실패했을 때 언제 다시 재시도 요청을 할 것인지에 대해 즉 재시도 시기 결정에 대해 구분하여 어떨 때 사용자 경험이 향상되는 지 분석해놓은 글도 있었다.
학습을 하고 나니 무작정 재요청을 보내는게 맞지 않다고 생각했지만, 응급 신고인 만큼 빠르게 요청을 성공시켜야한다고 생각하여 retry하는게 아니라 애초에 세션 대기 큐에 상황실 대원이 있는게 아니라 신고자가 대기 큐에 있어야하는게 아닌가 고민이 들었다.
사용자의 정보를 입력받는 폼에서 웹에서는 보통 하나의 페이지에서 입력을 받아 폼을 나눈다는 생각을 못했는데 모바일의 UI UX에 대해 찾아보면서 한 화면에 많은 input을 두지 않는 것을 알게 되었다.
페이지를 나누니 이전에 입력했던 input을 저장할 필요가 있었고, form의 validation, useState, onChange, onSubmit 코드가 계속 중복되어 불편했다.
그래서 form을 컴포넌트 외부에서 관리하는 useForm 훅을 만들었고 훅에서 value에 맞는 유효성 검사와 에러에 따른 helper text 설정, onSubmit, onChange handler 함수들의 상태를 관리하도록 리팩토링했다.
상태관리로 Zustand를 사용했기 때문에 zustand에 input store를 만들어서 훅을 통해 value와 validation 상태를 저장했다.
그냥 생각해서는 button이 disabled 되어있으면 active interaction은 당연히되지 않을거라고 생각했는데 작용한다.
스택오버플로우에서 :active doesn't exclude :disabled elements. 라는 답을 주었다.
styled-component를 사용중이었기 때문에 간단하게 disalbe 상태라면 active props를 주지 않는 식으로 해결했다.
고도화 한다면 웨어러블 기기나 스마트폰 자체의 센서를 통해 낙상을 감지하여 자동 신고를 한다던가,
현재는 nfc 스티커를 통해 태깅 신고를 하려면 타 앱을 설치하여 스티커에 url을 저장해야하는 복잡한 절차가 있기 때문에 자체 nfc writer 앱을 만들어 보는 것도 고도화할 부분이라고 생각한다.
사회 공헌적인 주제여서 인지 발표 덕분인지 삼성 청년 SW아카데미 공통 프로젝트 우수상을 수상하였다. 수상 욕심을 가지지 않고 시작한거라 얼떨떨하고 개인적으로 부족하다고 생각하여 더욱 좋은 모습을 못보여준게 아쉽기도 했다.
또한 백엔드 문제로 사용하지 못한 node.js 소켓 코드를 써보기 위해 직접 TURN 서버를 구현해 배포해보고 싶다.
안드로이드 개발