Google은 지난달에 공식
Google I/O 2017 for Android 앱의
소스 코드를 공개했습니다.
올해의 앱에서는 기존 기능이 상당히 수정되었고 여러 가지 새로운 기능이 추가되었습니다. 또한,
Firebase를 사용하도록 기술 스택도 확장되었습니다. 본 게시물에서는 이 앱에서 주목할 만한 몇 가지 변경 사항과 함께 설계 고려 사항을 집중적으로 살펴보겠습니다.
2017년도에 가장 주목할 만한 새로운 기능은 이벤트
예약 시스템이며, 이 시스템은 직접 참석하는 사람의 불필요한 시간 낭비를 없애주고 간소화된 컨퍼런스 환경을 제공하는 데 도움이 되도록 설계되었습니다. 등록된 참석자는 세션을 예약하고 컨퍼런스 진행 이전과 도중에 대기목록에 이름을 올릴 수 있었습니다. 이러한 예약을 통해 길게 줄을 서서 기다릴 필요 없이 신속한 세션 입장이 가능해졌습니다. 예약 데이터는 참석자의 컨퍼런스 배지와 동기화되었으므로, 이벤트 담당자가 NFC 지원 휴대폰으로 예약을 확인할 수 있었습니다. 예약 기능은 엄청나게 큰 인기를 끌었을 뿐만 아니라, 이벤트 담당자가 예약 데이터를 통해 실제 필요한 좌석 수에 맞게 I/O 진행 이전과 도중에 세션이 진행되는 공간의 크기를 적절히 바꿀 수 있었습니다.
예약 기능은
Firebase 실시간 데이터베이스(RTDB)와
Cloud Functions for Firebase를 사용하여 구현되었습니다. RTDB는 사용자 기기 간의 손쉬운 동기화를 지원했습니다. 그래서 우리는 데이터베이스 업데이트를 수신하는 리스너만 코드에 구현하면 되었습니다. RTDB가 기본 제공 오프라인 지원도 제공한 덕분에, 이동하는 동안 네트워크에 간헐적으로만 연결할 수 있었던 경우에도 컨퍼런스 데이터를 이용할 수 있었습니다. Cloud Function은 트랜잭션을 사용하여 상태의 정확성을 보장하고(문제를 일으키는 사용자가 너무 많은 좌석을 확보할 가능성 차단) 이벤트 예약 시스템과 통신하는 방식으로 백그라운드에서 예약 요청을 사용자 대신 처리해주었습니다.
전년도와 마찬가지로, 전체 앱 데이터에 대한 추상화 계층으로 ContentProvider를 사용했습니다. 따라서 RTDB 데이터를 ContentProvider와 통합하는 방법을 찾아야 했습니다. 우리는 데이터를 저장할 두 로컬 캐시, 즉 1) ContentProvider를 통해 액세스하는 기존의 로컬 SQLite 데이터베이스와 2) RTDB가 오프라인 액세스를 용이하게 하기 위해 생성하는 로컬 캐시 중 하나를 선택하는 문제를 두고 협상해야 했습니다. 우리는 ContentProvider 아래에 모든 앱 데이터를 통합하기로 결정했습니다. 사용자에 대한 예약 데이터가 RTDB에서 변경될 때마다 ContentProvider를 업데이트하여 ContentProvider가 항상 앱 데이터의 진정한 단일 소스가 되도록 했습니다. 즉, 사용자가 적극적으로 예약을 관리할 수 있는 Session Detail Activity라는 단일 화면에만 RTDB에 대한 연결을 개방된 상태로 유지해야 했다는 의미입니다. 앱의 다른 부분에 표시된 예약 데이터는 ContentProvider에 의해 처리되었습니다. 오프라인 모드에 있는 경우 또는 RTDB에 대한 연결이 신뢰할 수 없거나 지연되는 경우, ContentProvider에서 마지막으로 알려진 사용자 예약 상태만 가져올 수 있었습니다.
또한, RTDB를 IOSched의 전체 동기화 논리로 통합하기 위한 좋은 패턴을 찾아야 했습니다. 특히, RTDB에 앱에서 사용한 '핑하고 가져오기' 접근 방식과 매우 다른 동기화 모델이 함께 제공되기 때문입니다. 우리는 종전과 같이
Cloud Endpoints를 사용하여 기기 간은 물론, 웹 및 iOS 클라이언트와 사용자 데이터를 동기화하기로 결정했습니다(데이터 자체는
데이터 저장소에 저장되었음). RTDB가 기본 제공 데이터 동기화를 제공하지만, 우리는 사용자의 예약 데이터가 모든 기기에서 최신 상태인지를 확인하기를 원했습니다.
심지어 앱이 포그라운드에 있지 않을 때도 마찬가지입니다. 우리는 Cloud Function을 사용하여 RTDB 예약 데이터를 동기화 흐름에 통합했습니다. 사용자의 예약 데이터가 RTDB에서 변경되면 함수가 엔드포인트를 업데이트했습니다. 이를 통해 모든 사용자의 기기에 표시되는
Firebase 클라우드 메시징 다운스트림 메시지를 트리거하고, 그런 다음 데이터 동기화를 예약했습니다.
올해 제공한 이 앱에는 I/O에서 시시각각 개발 관련 정보를 사용자에게 알리는
피드 기능도 추가되었습니다. (대부분의 앱 사용자가 원격 사용자였으며, 피드는 이들 사용자에게 컨퍼런스 정보를 제공하는 창이었습니다.) 또한, RTDB로 피드 기능을 구동했으며, 여기서 데이터는 단순한 CMS를 사용하여 서버로 푸시되었습니다. 우리는 Cloud Function을 사용하여 RTDB 피드 데이터를 모니터링했습니다. 피드 데이터가 서버에서 업데이트되면 Cloud Function이 클라우드 메시징 다운스트림 메시지를 클라이언트로 전송했습니다. 이 클라이언트는 사용자에게 새로운 피드 항목을 시각적으로 표시해주었습니다.
2015년과 2016년에 우리는 IOSched용으로 MVP 아키텍처를 채택했으며 올해도 계속 사용했습니다. 이 아키텍처를 통해 관심사의 분리(SoC) 원칙을 훌륭하게 구현하고 테스트를 수월하게 진행할 뿐 아니라, 일반적으로 코드를 더욱 깔끔하고 쉽게 유지 관리할 수 있습니다. 피드 기능과 관련해서는 개념화하기가 아주 쉬울 뿐만 아니라 필요한 모듈성을 제공하는
Android Architecture Blueprints의 영향을 받아 더욱 가벼운 MVP 구현을 실험해 보기로 결정했습니다. 교육적인 측면과 실용적인 측면의 목표가 모두 있었습니다. 즉, 개발자를 위한 대체 MVP 패턴을 제공할 뿐 아니라, 이 기능에 대한 요구 사항에 적합한 아키텍처를 선보이고도 싶었습니다.
처음에는 IOSched가
Firebase 원격 구성을 많이 활용했습니다. 과거에 우리는 세션 이외의 데이터, 즉 와이파이 정보, 셔틀 스케줄, 카풀에 대한 할인 코드 등이 컨퍼런스 진행 전이나 도중에 변경된 경우 사용자에게 이를 알릴 수 없다는 점을 스스로 깨달은 적이 있었습니다. 앱 업데이트를 강제하는 것은 현실성이 없는 일이었으므로, 앱 내의 기본값만 업데이트 가능하도록 하고 싶었습니다. 원격 구성을 사용하여 이 문제를 쉽게 해결할 수 있었습니다.
마침내, 사용자에게 다음과 같이 변경 사항을 알리는 3계층 시스템을 구축할 수 있었습니다.
- 클라우드 메시징과 데이터 동기화를 통해 컨퍼런스 데이터와 사용자 데이터 변경 사항을 전달했습니다(핑하고 가져오기 모델).
- RTDB를 통해 피드 데이터 변경 사항을 제어했습니다.
- 앱 내의 상수에 대한 변경 사항은 원격 구성을 통해 제어했습니다.
향후 계획
2017년 코드를 발표했지만 앞으로 몇 개월 동안 계속해서 해야 할 일이 남아 있습니다. 백그라운드 처리를 위한 최신 패턴을 따르도록 코드를 업데이트할 예정이며(또한 앱을 'O'에 호환되도록 할 예정임), 향후 앱의 전반적인 디자인을 간소화하기 위해 Android의
아키텍처 구성 요소를 채택할 계획입니다. 개발자는
GitHub에서 코드 변경 사항을 계속 확인하실 수 있습니다.