ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 비동기 처리 2-5 (네트워크 통신 - retrofit)
    안드로이드 학습/Android 기술면접 대비 코드 2024. 11. 1. 10:48

    안드로이드에서 앞서 RxJava를 학습했던 것들은 UI 처리나 기타 비동기가 사용되야 하는 곳 등등을 학습했다.

    • UI 이벤트 처리 (실시간 검색, 버튼 클릭)
    • 네트워크 통신
    • 데이터베이스 
    • 그 외에 비동기 방식들

    이번에는 Retrofit과 함께 네트워크 통신을 하는 방법을 학습하려고 한다.

     

     

    사용한 앱 스택 :

    • RxJava & Retrofit
    • Hilt
    • databinding
    • ViewModel

    적용된 아키텍처 : 

    • Three Layer Architecture

     

    참고 :

    (1) developer 사이트 :  링크

    (2) 3Layer 예제 : https://github.com/tdcolvin/PlanetSpotters

     

    1. build.gradle(app)

    먼저 Rx와 Retrofit 그리고 Hilt 라이브러리를 추가해야 한다. 

    plugins {
        id 'com.android.application'
        id 'org.jetbrains.kotlin.android'
        id 'kotlin-kapt'
        id 'kotlin-android'
        id 'com.google.dagger.hilt.android'
    }
    
    buildFeatures {
        viewBinding = true
        dataBinding = true
        compose = true
    }
    
    dependencies {
    
    	... 생략 ...
     
        // RxJava & RxKotlin & RxAndroid & RxBinding
        implementation("io.reactivex.rxjava3:rxandroid:3.0.2")
        implementation("io.reactivex.rxjava3:rxjava:3.1.5")
        implementation("io.reactivex.rxjava3:rxkotlin:3.0.0")
        implementation("com.jakewharton.rxbinding4:rxbinding:4.0.0")
    
        // Retrofit
        implementation 'com.squareup.retrofit2:retrofit:2.11.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // JSON 데이터 처리를 위한 Gson 컨버터
        implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'  // RxJava 3 어댑터 추가
    
        // Dagger Hilt
        implementation "com.google.dagger:hilt-android:2.51.1"
        kapt "com.google.dagger:hilt-compiler:2.51.1"
    }

     

    2. build.gradle(Project)

    plugins {
        id 'com.android.application' version '8.1.4' apply false
        id 'org.jetbrains.kotlin.android' version '1.9.24' apply false
        id 'com.google.gms.google-services' version '4.4.2' apply false
        id 'com.google.dagger.hilt.android' version '2.51.1' apply false
    }

     

    3. retrofit 추가 

     

    3-1) RxRetrofitTestApi

    interface RxRetrofitTestApi {
    
        @GET("posts")  // 데이터를 한번만 받아오기 때문에 Single을 사용한다.
        fun getRxRetrofitTestData(): Single<List<RxRetrofitTestDTO>>
    }

     

    RxJava의 Base Classes중 Single을 사용했다. 왜냐하면 데이터를 한번만 가져올 것이기 때문이다. 

     

    3-2) RxRetrofitTestDTO

    data class RxRetrofitTestDTO(val userId:Int, val id:Int, val title:String, val body:String)

     

    데이터 형식에 맞게 DTO 생성. 

    더보기
    [
      {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
      },
      {
        "userId": 1,
        "id": 2,
        "title": "qui est esse",
        "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
      },
      
      ... 생략
     
    ]

    3-3) NetworkModulo

    @Module
    @InstallIn(SingletonComponent::class)
    class NetworkModulo {
    
        @Provides
        @Singleton
        fun provideRetrofit(): Retrofit {
            return Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .baseUrl(Constants.URLS.BASE_URL)
                .build()
        }
    
        @Provides
        @Singleton
        fun provideApiService(retrofit: Retrofit): RxRetrofitTestApi {
            return retrofit.create(RxRetrofitTestApi::class.java)
        }
    }

     

    의존성을 없애기 위해서 Dagger Hilt를 적용했다. 

    Retrofit에 먼저 의존성을 주입 후 RxRetrofitTestApi에도 의존성 주입.

     

    3-4) RxRetrofitTestRepository

    @Singleton
    class RxRetrofitTestRepository @Inject constructor(
        private val rxRetrofitTestApi: RxRetrofitTestApi
    ){
        fun fetchAllRxRetrofitTestData(): Single<List<RxRetrofitTestDTO>> {
            return rxRetrofitTestApi.getRxRetrofitTestData()
        }
    }

     

    Repository에서 데이터 통신을 관리한다. 

    현재 예제에서는 네트워크 통신한 데이터만 가져왔지만 Local DB에서 가져온 것도 Repository 같은 곳에서 같이 관리 할 수 있다. 

     

    3-5) RxRetrofitUseCases

    class RxRetrofitUseCases @Inject constructor(
        private val rxRetrofitTestRepository: RxRetrofitTestRepository
    ) {
    
        fun execute(): Single<List<RxRetrofitTestDTO>> {
            return rxRetrofitTestRepository.fetchAllRxRetrofitTestData()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        }
    }
    

     

    기존 MVVM과 다른 점이 usecase를 사용한다는 점이 다르다. ViewModel은 데이터 관리만 하고 비지니스 로직을 Usecase에서 처리하게 한다. 

     

    3-6) RxRetrofitTestViewModel

    @HiltViewModel
    class RxRetrofitTestViewModel @Inject constructor(private val rxRetrofitUseCases: RxRetrofitUseCases) :
        ViewModel() {
    
        private val _rxRetrofitTestData =
            MutableLiveData<RxRetrofitTestState<List<RxRetrofitTestDTO>>>()
        val rxRetrofitTestData: LiveData<RxRetrofitTestState<List<RxRetrofitTestDTO>>> get() = _rxRetrofitTestData
    
        private var disposable: Disposable? = null
    
        fun fetchAllData() {
            _rxRetrofitTestData.value = RxRetrofitTestState.loading()
            val observer = rxRetrofitUseCases.execute()
            disposable = observer.subscribe(
                { data -> _rxRetrofitTestData.value = RxRetrofitTestState.success(data) },
                { error -> _rxRetrofitTestData.value = RxRetrofitTestState.error(error.message) }
            )
        }
    
        fun dispose(){
            disposable?.dispose()
        }
    }

     

    자바의 SRP원칙에 따라 ViewModel은 데이터만 관리하게 만든다. 

    여기서 특이 했던 점은 데이터를 그대로 받는 것이 아니라 RxJava에서 

    데이터를 success, loading, error로 나눌 수 있게 데이터를 변경시켜줘서 사용한다는 점이다.

     

    이런 방식은 처음 사용해서 가독성이 더 좋아지는 것인지 또는 다른 장점이 있는 것인지 더 학습해봐야 겠다. 

     

    3-7) RxJavaRetrofitFragment

    class RxJavaRetrofitFragment :
        DataBindingBaseFragment<FragmentRxJavaRetrofitBinding>(R.layout.fragment_rx_java_retrofit) {
    
        private lateinit var rxRetrofitTestViewModel: RxRetrofitTestViewModel
        private val rxRetrofitTestAdapter by lazy { RxRetrofitTestAdapter() }
    
        override fun initContentInOnViewCreated() {
            rxRetrofitTestViewModel = ViewModelProvider(this)[RxRetrofitTestViewModel::class.java]
    
            rxRetrofitTestViewModel.rxRetrofitTestData.observe(this) { rxRetrofitTestState ->
    
                when (rxRetrofitTestState.status) {
                    Status.SUCCESS -> {
                        showLoadedData(rxRetrofitTestState.data)
                    }
    
                    Status.LOADING -> {
                        showLoadingView()
                    }
    
                    Status.ERROR -> {
                        showErrorMsg(rxRetrofitTestState.message)
                    }
                }
    
            }
    
            binding?.apply {
                rxRetrofitTestVM = rxRetrofitTestViewModel
    
                btnRxRetrofitLoad.setOnClickListener {
                    rxRetrofitTestViewModel.fetchAllData()
                }
            }
            
            ... 생략 ...
            
     }

     

    마지막 Fragment 부분이다. 

    LiveData를 Observe 패턴으로 데이터 변경을 감지하게 했다.

     

     

    RxJava와 Retrofit을 사용해서 네트워크 통신을 해봤다.

     

    하지만 해보면서 느낀 것은 새로 만드는 프로젝트에는 굳이? RxJava를 넣어야 하는가에 대한 물음이었다.

     

    프로젝트를 코루틴으로 하고 새로 만들어진 프로젝트라면 코루틴을 사용하는 것이 더 편하지 않을까 싶다.

    이번에는 코루틴에 대해서 좀더 학습해봐야 겠다. 

Designed by Tistory.