Kotlin Multi Platform Mobile에서 Decompose, MviKotlin 적용기 (2)

이태훈·2022년 2월 12일
0

안녕하세요.

이번 포스팅에서는 Decompose와 MviKotlin에서 데이터를 파싱하기 위해 작업을 해보겠습니다.

전체적인 구조는 다음과 같습니다.

Clean Architecutre를 적용하여 Data, Domain 두 개의 레이어로 나누었습니다.

먼저 Domain Layer를 보겠습니다.

interface TmdbRepository {

    suspend fun getMovieList(page: Int): Result<Movies>
}

DIP 조건을 충족시키기 위한 Repository의 Interface입니다.

class GetMovieListUseCase(
    private val tmdbRepository: TmdbRepository
) {

    suspend operator fun invoke(page: Int) = tmdbRepository.getMovieList(page)
}

다음으로 유저의 일련의 행동을 정의해주는 UseCase입니다.

간단히 데이터만 파싱하는 UseCase라 Repository의 함수를 그대로 사용해줍니다.

다음으로 Data Layer를 보겠습니다.

sealed class Result<T> {

    class Success<T>(val data: T) : Result<T>()

    class Error<T>(val msg: String?) : Result<T>()
}

@Serializable
data class Movies(
    val page : Long,
    @SerialName("total_pages")
    val totalPage : Long,
    @SerialName("total_results")
    val totalResults : Long,
    val results : List<Movie>
)

@Serializable
data class Movie(
    val id : Int,
    val overview : String,
    val popularity : Double,
    @SerialName("vote_count")
    val voteCount : Int,
    @SerialName("vote_average")
    val voteAverage : Double,
    @SerialName("original_language")
    val OriginalLanguage : String,
    @SerialName("backdrop_path")
    val backdropPath : String?,
    @SerialName("poster_path")
    val posterPath : String?,
    @SerialName("media_type")
    val mediaType : String,
    @SerialName("genre_ids")
    val genres: List<Int>
)

네트워킹 결과를 처리하기 위한 Result 클래스와 Remote API로부터 받을 데이터를 정의한 Movies, Movie 입니다.

Ktor Client에서 JSON Seralizer를 사용하기 위해 Kotlin Serialization을 사용했습니다.

class NetworkTmdbDataSource(
    private val httpClient: HttpClient
) {

    suspend fun getMovieList(page: Int): Result<Movies> {
        return try {
            httpClient
                .get("3/trending/all/day") {
                    parameter("page", page)
                    parameter("api_key", "...")
                }
                .body<Movies>()
                .toResult()
        } catch (e: Exception) {
            Result.Error(e.message)
        }
    }
}

fun <T> T.toResult() = Result.Success(this)

Ktor Client를 통해 Movie List를 파싱하는 함수입니다.
저는 베타버전인 2.0.0을 사용하여서 1.6.7 버전이랑은 코드가 상이합니다. 참고해주시길 바라겠습니다.

class TmdbRepositoryImpl(
    private val tmdbDataSource: NetworkTmdbDataSource
) : TmdbRepository {
    override suspend fun getMovieList(page: Int): Result<Movies> = tmdbDataSource.getMovieList(page)
}

Repository의 구현체입니다. 마찬가지로 MovieList만 얻어내고 캐싱이나 다른 작업은 안 해주기 때문에 간단히 Remote Data Source의 함수만 호출해줍니다.

koin v2

val networkModule = module {
	single { HttpClient {
		defaultRequest {
			url {
				protocol = URLProtocol.HTTPS
				host = "api.themoviedb.org"
			}
		}
		install(ContentNegotiation) {
			json(Json {
				ignoreUnknownKeys = true
			})
		}
	} }

	single {
		NetworkTmdbDataSource(get())
	}
}

val repositoryModule = module {
	single<TmdbRepository> { TmdbRepositoryImpl(get()) }
}

val interactorModule = module {
	single { GetMovieListUseCase(get()) }
}

koin v3

val networkModule = module {
	single { HttpClient {
		defaultRequest {
			url {
				protocol = URLProtocol.HTTPS
				host = "api.themoviedb.org"
			}
		}
		install(ContentNegotiation) {
			json(Json { ignoreUnknownKeys = true })
		}
	} }

	singleOf(::NetworkTmdbDataSource)
}

val repositoryModule = module {
	singleOf(::TmdbRepositoryImpl) { bind<TmdbRepository>() }
}

val interactorModule = module {
	singleOf(::GetMovieListUseCase)
}

Koin으로 module을 설정해줍니다.

마찬가지로, Ktor Client의 2.0.0 베타 버전을 사용하여 JsonSerializer 지정해주는 코드는 1.6.7 버전과 상이합니다.

다음 장에 이어서 본격적으로 Decompose와 MviKotlin을 적용하여 비즈니스 로직을 끝내고 뷰를 그려보도록 하겠습니다.

profile
https://www.linkedin.com/in/%ED%83%9C%ED%9B%88-%EC%9D%B4-7b9563237

0개의 댓글