[Android] 기억에 남는 장소 저장 앱 개발기 - 5

송규빈·2022년 8월 18일
0

DayToGo 개발기

목록 보기
5/7

Mapper

Firebase RealTimeDatabase에서 데이터를 가져오는 도중 문제가 발생했다.
값이 파베에 저장 되어있을 때 json형태(key,value)이기 때문에 값이 내려올 때는 HashMap<String,Any>형태로 내려오게 된다.


(대략 이런식으로 내려온다.)

근데 아무런 조치도 안 하고 이 HashMap을 다루려다 보면 코드가 굉장히 더러워진다.
일단 값을 빼내오기 위해서는 string값으로 가져와야 할 뿐더러 DTO를 만든 의미가 없어지기 때문이다.

그래서 mapper 클래스를 만들어서 그 안에서 자바의 Reflection으로 사용하여 DTO 형태로 변환시키기로했다.

object LocationInfoMapper {

    fun firebaseResponseToLocationInfo(hashMap: HashMap<String, Any>): LocationInfo {
        val locationInfo = LocationInfo()
        val clazz = LocationInfo::class.java
        val fields = clazz.declaredFields
        fields.map {
            it.also { field -> field.isAccessible = true }
        }.forEach {
            val value = hashMap[it.name]
            it.set(locationInfo, value)
        }
        return locationInfo
    }
}

그리고 이것을 아래와 같은 형태로 변환시켜 LocationInfo 형태로 반환시켰다.

 .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .map {
                val locationInfoList = it as List<HashMap<String,Any>>
                locationInfoList.map {  hash->
                    LocationInfoMapper.firebaseResponseToLocationInfo(hash)
                }
            }

처음에는 HashMap의 확장함수를 만들까...그냥 알아보기 쉽게 typealias를 사용하여 HashMap<String,Any>를 관리할까... 생각을 하다가 침대에 눕자마자 자바의 Reflection이 생각나서 위와 같은 방법을 택했다.

그냥 enum 클래스의 value를 키값으로 설정하여 값을 가져오는 방식으로 바꿨다. 왜냐..리플렉션을 사용하게 되면 런타임 시 동적으로 클래스가 할당되기 때문에 GC가 일어나 성능이 좋지 않기 때문이다.

사용자가 앱에 기억시켜놓은 장소들은 navermap 위에 마커를 사용하여 표시하기로했다.
그래서 찾아봤더니 공식문서에서는 marker 객체를 생성할 때는 메인스레드에서 작업하는 것을 지양한다고 적혀있었다.
(뭐 당연한 얘기겠지만...)
그래서 Rx를 이용하여 스레드 관리를 했다.

viewModel.savedLocationList.observe(this) { stationList ->
            Observable.fromArray(*stationList.toTypedArray())
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.computation())
                .filter {
                    // filtering empty title and description
                    it.title.isNotEmpty() || it.description.isNotEmpty()
                }.map { location ->
                    // 클릭 시 장소 정보를 보여주기 위해 locationId와 함께 locationInfo 저장
                    viewModel.setSavedLocationInfo(location)
                    // 저장된 위치에 마커 생성
                    Marker().apply {
                        position = LatLng(location.latitude, location.longitude)
                        icon = OverlayImage.fromResource(R.drawable.check)
                    }
                }
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { marker ->
                    marker.map = naverMap
                }.addTo(disposable)
        }

장소의 별명과 설명이 비어있는 데이터를 필터링하고 마커 객체를 생성하는 과정은 computation 스케쥴러로 관리했고, 생성된 마커를 navermap에 적용하는 건 ui 작업이기 때문에 main 스레드에서 관리하였다.

profile
🚀 상상을 좋아하는 개발자

0개의 댓글