[TIL] 231102 회고

서정한·2023년 11월 2일
0

내일배움캠프 7기

목록 보기
64/66
post-thumbnail

Intro

  • 저번시간 선언형 프로그래밍에대해 알아본 후 본격적으로 구현을 좀 해봤다.
  • 현재 우리 프로젝트에서 실시간으로 가져와야할 데이터는 게시글 목록이므로 여기에 코루틴과 Flow를 적용해보았다.

들어가기에 앞서

  • 근데 왜 꼭 Flow여야할까? LiveData로 그냥쓰면 안될까? 라는 궁금증이 생겨 검색을 해본결과 몇가지 이유들이 나오게되었다.
    1. Flow의 컨셉
      • Flow는 비동기 데이터스트림을 처리하기위해 만든 기능이다. Flow는 코루틴을 기반으로 빌드된다. 비동기 문제를 해결하기위해 Rx가 나왔고, 그 이후 코루틴이 등장하여 문제를 해결했다. 즉, Flow는 애초에 비동기 프로그래밍에서 사용하기위해 고안된 녀석이다.
    2. LiveData vs Flow
      • LiveData: ViewModel에서 통상 사용하고 데이터의 상태를 UI에 실시간으로 반영할 수 있다. LifeCycleOwner 개념이있어 Owner의 Lifecycle을 따른다. Lifecycle의 상태에따라 Observing을 알아서 중단해준다. Lifecycle을 인식하기에 메모리 누수가 일어나지않는다.
      • Flow : 이전에 개발자들은 비동기처리를 위해 Rx를 사용했다고합니다. 이후 Coroutine Flow API가 출시되었는데, Flow는 순차적으로 실행되는 데이터스트림을 비동기적으로 처리할 수 있는 특징을 가지고있다.

정리하자면...!

  • LiveData는 ViewModel과 View 사이의 상호작용을 담당하고, 다른 Layer(UseCase, Repository, DataSource)에서는 Flow를 사용하여 데이터를 수집하고 전달해주는 역할을 가진다. Flow는 생명주기를 인식하지못하기에 ViewModel에서 사용하기에는 메모리 누수등의 위험성이있다.
  • 코루틴도 선언적 프로그래밍의 패러다임을 따라 만들어졌다. 그래서 안드로이드 공식문서를보면 생산자 -> 중간자 -> 소비자 이런형태의 그림으로 설명하고있는 모습이 있다. 생산자가 데이터를 생산해서 공급해주면 중간자에서 내가 원하는 데이터만 map 혹은 filter 등을 사용하여 간편하게 선택하고 그 이후 소비자에게 전달해주는 형태이다.

그래서 어떻게 개발했냐면..

/// DataSource부분 
    fun getAllBoards(): Flow<List<CommunityModel?>> {
        val ref = getReference()
        return ref.snapshots.map { snapshot ->
            snapshot.children.mapNotNull {
                it.getValue<CommunityModel>()
            }
        }
    }
  • 엥? 이라고 할것같다. 현재 내가 진행중인 프로젝트에서는 Firebase RealtimeDatabase를 사용중이다. 여기에서 최근 업데이트를 하나 발표했는데 기존에 사용하던 CallbackFlow를 래핑해서 편하게 사용할수있도록 만들어주었다. 그래서 이렇게 사용한것이다. 위에 선언한 snapshots를 들어가보면 내부적으로 callbackFlow를 사용하는것을 볼 수 있다.
val Query.snapshots
  get() =
    callbackFlow<DataSnapshot> {
      val listener =
        addValueEventListener(
          object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
              repo.scheduleNow { trySendBlocking(snapshot) }
            }

            override fun onCancelled(error: DatabaseError) {
              cancel(message = "Error getting Query snapshot", cause = error.toException())
            }
          }
        )
      awaitClose { removeEventListener(listener) }
    }
  • 이후 받는쪽에서는 아래와같이 처리하면 끝! 별 내용은 없다. Flow는 collect를 선언하지않으면 그전까지는 데이터를 받지 않는다고한다. 그래서 사용할때 꼭 선언해줘야한다. 이후 내부에서 LiveData에 받은 데이터를 전달해주면된다.
    suspend fun getAllBoards() {
        getAllBoardsUseCase.invoke().collect() {
            _boards.value = it.toEntity()
        }
    }
  • 사실 이것말고도 적용한곳이 몇군데 있긴하지만 다 비슷한 내용이기에 이것만 가져와봤다.

callbackFlow

  • 공부하다가 새롭게 발견한 단어이다. 주 용도는 이름에서 보이듯 콜백에서 Flow를 사용하기위해 만들어진 기능이다.
  • 일단은 callback상황에서 Flow로 데이터를 넘겨줄수있다정도로만 이해하였고 이부분은 게시판 코드가 정리되면 한번더 살펴보려고한다.

Outro

  • 선언적 프로그래밍과 코루틴 그리고 Flow는 아직까지 신세계다.. 최대한 마음을 열고 이해하려고하는데 머리속은 아직 객체지향 그 어딘가를 떠도는중인 기분이다. 잘 공부하고 정리해야겠다 ..!
profile
잘부탁드립니다!

0개의 댓글