2024.01.31(수) TIL

quinones·2024년 1월 31일

오늘은 기존에 구현했던 kakaoAPI를 이용해서 이미지와 동영상을 가져오는 부분에 MVVM패턴을 적용시켜보았다.

이미지,동영상 가져오는부분MVVM적용시키기

기존코드

기존에 ImageSearchFragment에서 사용하던 코드이다.

@SuppressLint("NotifyDataSetChanged")
private fun communicateNetWork(param: HashMap<String, String>) = lifecycleScope.launch() {
    val authKey = "KakaoAK ${Contract.API_KEY}"
    //기존 검색기록은 삭제
    items.clear()

    val imageResponseData = NetWorkClient.combinedNetWork.getImageResults(authKey, param)
    val videoResponseData = NetWorkClient.combinedNetWork.getVideoResults(authKey, param)

    imageResponseData.searchDocument?.let {
        items.addAll(it.map { CombinedSearchItem(it, SearchItemType.IMAGE) })
    }
    videoResponseData.searchDocument?.let {
        items.addAll(it.map { CombinedSearchItem(it, SearchItemType.VIDEO) })
    }
    items.shuffle()
    items.sortedByDescending {
        when (it.itemType) {
            SearchItemType.IMAGE -> (it.searchItem as SearchDocument).datetime
            SearchItemType.VIDEO -> (it.searchItem as SearchDocumentVideo).datetime
        }
    }

    searchAdapter = SearchAdapter(items)
    binding.searchRecyclerView.adapter = searchAdapter
    searchAdapter.notifyDataSetChanged()
    searchAdapter.itemClick = object : SearchAdapter.ItemClick {
        override fun onClick(item: CombinedSearchItem) {
            val selectedThumbnail: String
            val selectedSiteName: String
            val selectedTime: String
            when (item.itemType) {
                SearchItemType.IMAGE -> {
                    val result = item.searchItem as SearchDocument
                    selectedThumbnail = result.thumbnail_url
                    selectedSiteName = result.display_sitename
                    selectedTime = result.datetime
                    result.isLike = !result.isLike
                }

                SearchItemType.VIDEO -> {
                    val result = item.searchItem as SearchDocumentVideo
                    selectedThumbnail = result.thumbnail
                    selectedSiteName = result.title
                    selectedTime = result.datetime
                    result.isLike = !result.isLike
                }
            }
            val selectedItems = SelectedItem(selectedThumbnail, selectedSiteName, selectedTime)
            if (SelectedItem.myLikeList.contains(selectedItems)) {
                SelectedItem.myLikeList.remove(selectedItems)
            } else {
                SelectedItem.myLikeList.add(selectedItems)
            }
        }

    }
}
...
private fun setUpImageParameter(input: String, page:Int): HashMap<String, String> {
    val authKey = "KakaoAK ${Contract.API_KEY}"
    return hashMapOf(
        "Authorization" to authKey,
        "query" to input,
        "sort" to "recency",
        "page" to page.toString(),
        "size" to "20"
    )
}

ViewModel생성

imageSearchFragment에 대한 ViewModel을 만들어준다. 이름은 SearchViewModel로 만들어줬다.

class SearchViewModel : ViewModel() {
    private val _searchParam: MutableLiveData<HashMap<String, String>> = MutableLiveData()
    val searchParam: LiveData<HashMap<String, String>> get() = _searchParam
    private val _searchResult: MutableLiveData<List<CombinedSearchItem>> = MutableLiveData()
    val searchResult: LiveData<List<CombinedSearchItem>> get() = _searchResult

    fun setUpParameter(input: String, page: Int) {
        val authKey = "KakaoAK ${Contract.API_KEY}"
        _searchParam.value = hashMapOf(
            "Authorization" to authKey,
            "query" to input,
            "sort" to "recency",
            "page" to page.toString(),
            "size" to "20"
        )
    }
    fun dataFromNetwork(param: HashMap<String, String>) {
        viewModelScope.launch {
            val authKey = "KakaoAK ${Contract.API_KEY}"
            val items = mutableListOf<CombinedSearchItem>()

            val imageResponseData = NetWorkClient.combinedNetWork.getImageResults(authKey, param)
            val videoResponseData = NetWorkClient.combinedNetWork.getVideoResults(authKey, param)

            imageResponseData.searchDocument?.let {
                items.addAll(it.map { CombinedSearchItem(it, SearchItemType.IMAGE) })
            }
            videoResponseData.searchDocument?.let {
                items.addAll(it.map { CombinedSearchItem(it, SearchItemType.VIDEO) })
            }

            items.shuffle()
            items.sortedByDescending {
                when (it.itemType) {
                    SearchItemType.IMAGE -> (it.searchItem as SearchDocument).datetime
                    SearchItemType.VIDEO -> (it.searchItem as SearchDocumentVideo).datetime
                }
            }

            _searchResult.value = items
        }
    }

ViewModel()을 상속해서 만들어준다. 사용했던 함수를 대부분 그대로 가져와서 사용해주고 return값을 넣어주는 대신 _value값에 리턴할 값을 넣어준다. value는 _value값을 받아올거고, 뷰모델이아닌 뷰에서 observe를 이용해서 상태변화를 감지하고 값을 받아와줄거다.

뷰에서 활용

searchParam 결과값을 받아와서 communication에 해당값을 넣어서 결과값을 가져와야한다.

@SuppressLint("NotifyDataSetChanged")
private fun initViewModel() =with(viewModel){
    searchParam.observe(viewLifecycleOwner){
        communicateNetWork(it)
    }
    searchResult.observe(viewLifecycleOwner){
        items.clear()
        items.addAll(it)
        searchAdapter.notifyDataSetChanged()
    }
}

onViewCreated에서 initViewModel를 실행시켜주고, 여기서 searchParam의 값을 옵저빙해서 검색됐을때의 값을 가져와준다.(it) 이 값을 communicateNetWork에 넣어서 결과를 받아와준다.

@SuppressLint("NotifyDataSetChanged")
private fun communicateNetWork(param: HashMap<String, String>) {
    viewModel.dataFromNetwork(param)
}

그리고 여기서 나온 값 역시 옵저빙을 해서 값이 변화하면 감지해서 initViewModel()에서 searchResult를 설정해준다.
기존 아이템을 지우고, 받아온 사진,동영상들을 아이템에 다 추가해주고, 리사이클러뷰를 업데이트시켜줬다.
이렇게해서 기존에 MVC형태로 만들어졌던 코드를 기능은 viewModel에서, 뷰는 View에서 관리를 시켜줬다.

키값 숨기기

build.gradle.kts파일에서 키값을 숨기는법이다.
키값을 숨기지않고 그대로 깃허브에 올리면 보안상의 문제가 있기때문에 해당부분은 올리지말아야한다.

local.properties

kakao_api_key = "숨길 키값"

이 파일 안에서 숨길 키값을 적어준다. 나는 kakao Rest키값을 숨겼다.

build.gradle.kts

안에서 함수를 만들어준다.

fun getApiKey(propertyKey: String): String{
    return gradleLocalProperties(rootDir).getProperty(propertyKey)
}

그리고 아래 위치에 추가해준다.

android{
	defaultConfig{
    	buildConfigField("String", "kakao_api_key", getApiKey("kakao_api_key"))
    }
    buildFeatures {
        buildConfig = true
    }
}

이렇게 추가하고 sync를 한 후에 상단바에 Build -> cleanProject, RebuildProject를 해준다.

사용법

const val API_KEY = BuildConfig.kakao_api_key

이렇게 사용해주면된다.


mvvm
MVVM
디자인패턴
github에key값숨기기
깃허브에키값숨기기

profile
이우진

0개의 댓글