Skip to content

⚙ [기술 분석] Firestore 쿼리 전략

박봉팔 edited this page Dec 2, 2024 · 1 revision

image

Firestore의 한계

Firestore의 경우 NoSQL기반의 데이터 베이스이기 때문에 관계형 데이터 베이스처럼 여러 테이블을 join해 함께 쿼리하는게 불가능 했다.

데이터 구조를 다시 설계하기엔 FirestoreStorage에 대한 조사를 진행하며, NoSQL에서 사용하는 데이터 구조 설계 방식에 대한 조사까지 진행하기엔 힘들다고 판단해 우선 기존의 RDB방식 구조를 사용해 작업을 진행하고, 추후 NoSQL기반의 구조로 변경하기로 했다.


1. DataSource분리

작업을 진행하며 가장 중점적으로 고려한 부분은 추후 수정에 대비해, 변경에 유연하게 대처할 수 있도록 하는 것이었다. 또한 DB와 통신하는 작업을 API를 제공하는 것처럼 인터페이스로 제공하고, 이에 따라 팀원들이 함수 하나로 손쉽게 데이터를 요청할 수 있도록 하는 것을 목표로 작업을 진행했다.

Firestore의 경우 REST API방식처럼 요청을 보내고 받아오는 방식이 아니었기 때문에, 각각의 Collection별로 DataSource로 분리해 인터페이스를 작성했다.


2. Repository에서 취합

UI에 표시되어야 할 값을 여러 DataSource에서 가져와야 하기 때문에 Repository에서 이를 취합해 UI에 표시될 모델로 매핑해 전달하도록 만들었다.

우선 Post를 가져오는 것부터 진행했다. 제일 먼저 Post의 ID를 알아야 하기 때문에 이를 먼저 가져온 뒤 해당 ID를 기반으로 Tag와 Reaction을 가져온 뒤 이를 매핑하는 방식으로 진행했다.

(Follow기능의 경우 미구현 상태이기 떄문에 추후에 추가 할 예정)


3. 비동기 작업으로 속도 개선

Collection을 통합해서 쿼리를 전송할 수 없기 때문에 기본적으로 하나의 Post를 불러오는데도 쿼리를 많이 요청해야 했고, Home화면의 경우 초기에 10개의 Post를 불러오기 때문에 로딩시간이 평균적으로 6~7초로 너무 길었다.

image

어차피 데이터를 불러오려면 모든 쿼리 요청을 보내야 했고, postId값만 있다면 이후에는 순서에 상관없이 데이터를 요청해도 상관 없었기 때문에 코루틴을 사용해 post를 가져오는 작업 이후 모든 요청을 비동기로 요청하도록 변경했다.

val tagsDeferred = async {
		posts.map {
        async {
            tagDataSource.getPostTag(it.postId).getOrElse {
                throw IOException("태그 불러오기 실패")
            }
        }
}.awaitAll()

val reactionsDeferred = async {
    posts.map {
        async {
            reactionDataSource.getReactionByPostId(it.postId).getOrElse {
                throw IOException("리액션 불러오기 실패")
            }
        }
}.awaitAll()

그 결과 로딩 시간을 평균 2초 정도로 단축시킬 수 있었다.

image


Try

기본적으로 쿼리 요청이 너무 많아 다운로드 시간도 오래걸릴 뿐더러 Firestore의 경우 요청 쿼리 횟수를 기준으로 가격이 책정되기 때문에 NoSQL기반의 데이터 구조를 통해 개선이 시급할 듯 하다.

Clone this wiki locally