기존에 개발하던 서비스의 고질적인 문제는 다량의 데이터와의 상호작용시 시간이 상당히 오래 걸린다는 것이었다.
Firebase RDB와의 통신에서의 속도 저하에 이어 Firebase RDB로부터 로드한 데이터를 Room에 집어넣는 과정에서의 2차 속도저하가 있었기 때문이었다.
하지만 여차저차 그동안의 서비스를 유지할 수 있던 점은, 다량의 데이터를 Query할 UI/UX가 없었기 때문이었다.
이제 신규 개발안에 따라 결론적으로 '진작에' 해결됬어야 할 문제와 일기토를 해야 될 상황에 이르렀다.
우선 기존의 로직 구조를 먼저 설명하고자 한다.
Before
- Firebase RDB에서 타겟으로 하는 테이블의 Data를 addValueEventListener를 통해 요청한다.
- 요청을 통해 들어온 DataSnapshot을 for 문 루프를 통해 하나씩 Rx Flowable 데이터 객체로 emit 한다.
- Emit 된 데이터 객체는 subscriber가 받는대로 즉시 Room에 개별적으로 Insert한다.
기존에는 Rx를 활용해 Firebase로부터 데이터를 수신하고, 이를 Room db에 업데이트 하는 Flow가 갖추어져 있었다.
이는 적은 양의 데이터 통신을 수행할 때는 이 구조의 효율성에 대해 인식하게 되는 계기가 많지 않았다.
하지만 이 구조를 가지고 약 5만개의 데이터를 상호작용한다는 것은 다른 이야기였다.
5만개에 달하는 데이터를 Firebase로부터 수신하고, 이를 Room에 삽입하는데 걸린 시간은 대략 3분에서 5분.
이는 사용자에게 충격적인 불편을 초래하는데 충분했고, 이를 보완하기 위한 추가적인 UI/UX 공수가 생길 판이었다.
어떻게 이 문제를 해결할 수 있을까. 하루를 개발팀과 함께 고군분투한 결과, 다행스럽게도
Firebase와 Room DB 양쪽에서 최적화에 좋은 요소들을 찾아낼 수 있었다.
1. 첫번째로, 기존 Firebase에서 대체해준 것은 addListenerForSingleValueEvent() 였다.
이는 구글의 도큐먼트와 같이 한번 호출한 뒤 수행하지 않을 경우에 사용하도록 권장하고 있다.
기존 사용자가 로그아웃을 한 이후 재로그인 할 때 불러와야 하는 정보들이었기에, 지속적으로 Listen을 할 필요는 없었다.
여기에 더불어 for loop로 DataSnapshot에 달린 데이터들을 개별적으로 Emit하지 않고, List 형태로 통째로 subscriber에게 넘겨주도록 수정했다.
2. 두번째로, for loop로 개별적으로 데이터를 Insert 하던 구문을 List로 쌓은 데이터 리스트를 그대로 Insert하고, 이를 Transaction 어노테이션 내에서 수행하도록 했다.
로그를 확인하면서 파악했던 건 Firebase와의 통신이 이미 끝난 뒤에도 Room에 데이터를 집어넣는 과정이 굉장히 더뎠다는 것이다.
Firebase와의 통신이 보통 2분 안에 마무리 되었다면, 이를 Room에 집어넣는 데에는 여기에 체감상 두 배가 넘는 시간이 소요되고 있었다.
이를 해결하기 위해 데이터를 개별적으로 밀어넣던 Insert 구문을 List로 가공한 데이터 셋을 Insert로 집어넣게 함과 동시에 Transaction 어노테이션 내에서 이를 수행하도록 했다.
결과는 눈에 띄게 달라졌는데, 기존에 Firebase 통신 이후 두 배 가량 걸리던 시간이 Firebase 통신에 소요된 시간과 유사한 정도, 혹은 그보다도 적게 소요되는 것을 확인할 수 있었다. (이후 가능하다면 수치화해서 기록해 볼 예정)
이번 개선 작업을 하면서 얻은 교훈은 두가지다.
1. 어떠한 데이터 작업이던지 간에 Transaction을 최소화 하자!
2. Room은 List 형태의 데이터를 그대로 Insert 할 수 있다!
< 참조 >
'ANDROID > DB' 카테고리의 다른 글
[안드로이드] Room 데이터베이스의 테이블 등을 업데이트 할 시, Migration 옵션을 필히 집어넣자. (0) | 2020.02.06 |
---|