Kotlin Multiplatform은 크로스 플랫폼 개발의 미래입니까? 시작하는 방법에 대한 팁
게시 됨: 2021-01-29요즘은 앱을 더 빨리 출시하려는 모바일 개발의 추세를 관찰할 수 있습니다. Android, iOS 등 다양한 플랫폼 간에 공통 코드 부분을 공유하여 개발 시간을 단축하려는 시도가 많이 있었습니다. 일부 솔루션은 이미 인기를 얻었지만 다른 솔루션은 아직 개발 중입니다. 오늘 저는 두 번째 그룹의 최신 접근 방식 중 하나인 Kotlin Multiplatform Mobile(줄여서 KMM)에 대해 논의하고자 합니다.
코틀린 멀티플랫폼 모바일이란?
KMM은 기본적으로 플랫폼 간에 비즈니스 로직을 공유하는 것을 목표로 하는 SDK입니다 . 대부분의 경우 어쨌든 동일해야 하는 부분입니다. 이것은 공유 모듈에 대한 여러 컴파일러 세트 덕분에 달성됩니다. 예를 들어 Android 대상은 Kotlin/JVM 변형을 사용하고 iOS에는 Kotlin/Native 변형이 있습니다. 그런 다음 공유 모듈을 일반적인 기본 앱 프로젝트에 추가할 수 있으며 UI를 담당하는 개발자는 Android용 Android Studio 및 iOS용 Xcode와 같은 친숙한 환경에서 사용자에게 최상의 경험을 제공하는 데 집중할 수 있습니다.
Kotlin 다중 플랫폼 대 Flutter
현재 크로스 플랫폼 앱 개발을 위한 가장 인기 있는 솔루션 중 하나는 Flutter입니다. "하나의 앱을 작성하고 모든 곳에서 실행" 규칙에 초점을 맞추고 있습니다. 이 규칙은 작동하지만 간단한 앱에만 해당됩니다. 실제 시나리오에서 개발자는 일부 플러그인이 누락된 경우와 같이 간격을 채우기 위해 어쨌든 각 플랫폼에 대한 네이티브 코드를 작성해야 하는 경우가 많습니다 . 이 접근 방식을 사용하면 앱이 다른 플랫폼에서 동일하게 표시되며 때로는 바람직하지만 경우에 따라 특정 디자인 지침을 위반할 수 있습니다.
나만의 앱을 만들 준비가 되셨나요?
Flutter 선택비슷하게 들릴지 모르지만 Kotlin Multiplatform은 크로스 플랫폼 솔루션이 아닙니다. 바퀴를 재발명하려고 하지 않습니다. 개발자는 자신이 알고 좋아하는 도구를 계속 사용할 수 있습니다. 네트워크 요청, 데이터 저장 및 기타 비즈니스 로직과 같이 이전에 여러 번 작성했어야 하는 코드 부분을 재사용하는 프로세스를 단순화합니다 .
코틀린 멀티플랫폼의 장단점
KMM의 장점 :
- 개발된 앱은 각 플랫폼에 대해 100% 기본이며 현재 사용되는 코드 및 타사 라이브러리와 쉽게 통합됩니다.
- 사용하기 쉽습니다. 거의 모든 Android 개발자가 이미 Kotlin을 사용하고 있으므로 시작하는 데 필요한 추가 지식이 거의 없습니다.
- 각 대상 플랫폼에 대해 UI를 분할할 수 있습니다 . 앱은 주어진 생태계와 일관성을 느낄 것입니다.
- 공유 로직 을 통해 개발자는 두 운영 체제에서 동시에 새로운 기능을 추가하고 버그를 수정할 수 있습니다.
KMM의 단점 :
- 많은 구성 요소가 아직 알파/베타 단계에 있으며 잠재적으로 불안정하거나 향후 변경될 수 있습니다.
어떤 회사가 KMM을 사용합니까?
공식 사이트에 따르면 이 기술에 대한 기업의 관심이 높아지고 있으며 목록은 계속해서 길어지고 있습니다. 그 중에는 Autodesk, VMWare, Netflix 또는 Yandex와 같은 잘 알려진 브랜드가 있습니다.
Kotlin 다중 플랫폼을 시작하는 방법은 무엇입니까?
심도 있는 정보를 얻을 수 있는 가장 좋은 곳은 공식 가이드이지만 이 기사에서는 앱을 가져오고 표시하는 "Hello World"보다 훨씬 간단하지만 더 흥미로운 예를 보여 드리고자 합니다. Randall Munroe의 최신 만화(CC BY-NC 2.5 라이선스)와 xkcd.com API의 제목.
다룰 기능:
- 프로젝트 설정
- 공유 모듈의 네트워킹
- Android 및 iOS 모두를 위한 간단한 UI
참고: 이 샘플은 Android 및 iOS 개발자 모두가 쉽게 읽을 수 있기를 원했습니다. 그래서 어떤 곳에서는 진행 상황을 명확히 하기 위해 일부 플랫폼별 모범 사례를 의도적으로 생략했습니다.
프로젝트 설정
먼저 이 프로젝트를 빌드하는 데 둘 다 필요하므로 최신 버전의 Android Studio 및 Xcode가 설치되어 있는지 확인합니다 . 그런 다음 Android Studio에서 KMM 플러그인을 설치합니다. 이 플러그인은 많은 것을 단순화합니다. 새 프로젝트를 만들려면 새 프로젝트 만들기를 클릭하고 KMM 응용 프로그램을 선택하기만 하면 됩니다.
프로젝트가 생성되면 공유 디렉토리에서 build.gradle.kts
파일로 이동합니다 . 여기에서 필요한 모든 종속성을 지정해야 합니다. 이 예에서는 네트워킹 계층에 kotlinx.serialization
를 사용하고 백엔드에서 json 응답을 구문 분석하기 위해 kotlinx.serialization을 사용하고 이 모든 작업을 비동기적으로 수행하기 위해 kotlin 코루틴을 사용합니다.
단순화를 위해 아래에 이미 존재하는 종속성에 추가해야 하는 모든 종속성을 보여주는 목록을 제공합니다. 종속성을 추가할 때 프로젝트를 동기화하기만 하면 됩니다(프롬프트가 나타남). 먼저 플러그인 섹션에 직렬화 플러그인을 추가합니다.
플러그인 { kotlin("plugin.serialization") 버전 "1.4.0" }
그런 다음 종속성을 추가합니다.
소스 세트 { val commonMain { 종속성 { 구현("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0") 구현("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt-2") 구현("io.ktor:ktor-client-core:1.4.1") 구현("io.ktor:ktor-client-json:1.4.1") 구현("io.ktor:ktor-client-serialization:1.4.1") } } val androidMain { 종속성 { 구현("io.ktor:ktor-client-android:1.4.1") } } val iosMain { 종속성 { 구현("io.ktor:ktor-client-ios:1.4.1") } } }
이 기사가 작성되는 시점에 iOS의 안정적인 버전의 코루틴 라이브러리에 몇 가지 문제가 있다는 점을 언급할 가치가 있습니다. 이것이 사용된 버전에 native-mt-2 접미사(네이티브 멀티스레딩을 나타냄)가 있는 이유입니다. 여기에서 이 문제의 현재 상태를 확인할 수 있습니다.
공유 모듈의 네트워킹
먼저 응답을 나타내는 클래스가 필요합니다. 해당 필드는 백엔드에서 반환된 json에 있습니다.
kotlinx.serialization.Serializable 가져오기 @직렬화 가능 데이터 클래스 XkcdResponse( 발 img: 문자열, val 제목: 문자열, 발 일: Int, val 월: Int, 발 연도: Int, )
다음 으로 HTTP 클라이언트를 사용하여 API를 나타내는 클래스를 만들어야 합니다 . json에 있는 모든 필드를 제공하지 않은 경우 ignoreUnknownKeys
속성을 사용하여 직렬 변환기가 누락된 필드를 무시할 수 있습니다. 이 예제에는 일시 중단된 함수로 표시되는 끝점이 하나뿐입니다. 이 수정자는 이 함수가 비동기식임을 컴파일러에 알립니다. 플랫폼별 코드로 더 자세히 설명하겠습니다.
io.ktor.client.* 가져오기 가져오기 io.ktor.client.features.json.* 가져오기 io.ktor.client.features.json.serializer.* io.ktor.client.request 가져오기* 클래스 XkcdApi { 개인 val baseUrl = "https://xkcd.com" 개인 값 httpClient = HttpClient() { 설치(JsonFeature) { 직렬 변환기 = KotlinxSerializer( kotlinx.serialization.json.Json { ignoreUnknownKeys = true } ) } } 재미있는 일시 중단 fetchLatestComic() = httpClient.get<XkcdResponse>("$baseUrl/info.0.json") }
네트워크 계층이 준비되면 도메인 계층으로 이동하여 데이터의 로컬 모델을 나타내는 클래스를 생성할 수 있습니다. 이 예에서는 추가 필드를 건너뛰고 이미지에 대한 만화 제목과 URL만 남겼습니다.
데이터 클래스 ComicModel( val imageUrl: 문자열, val 제목: 문자열 )
이 계층의 마지막 부분은 네트워크 요청을 트리거한 다음 응답을 로컬 모델에 매핑하는 사용 사례를 만드는 것입니다.
클래스 GetLatestComicUseCase(비공개 xkcdApi: XkcdApi) { 일시 중단 재미 실행() = xkcdApi.fetchLatestComic() .let { 코믹모델(it.img, it.title) } }
Android용 간단한 UI
androidApp
디렉토리 로 이동할 시간입니다. 기본 Android 앱이 저장되는 곳입니다. 먼저 여기에 있는 다른 build.gradle.kts
파일에 일부 Android 관련 종속성을 추가 해야 합니다. 다시 말하지만, 아래 목록은 이미 존재하는 종속성에 추가되어야 하는 종속성을 보여줍니다. 이 앱은 Model-View-ViewModel 아키텍처(처음 두 줄)를 사용하고 Glide를 사용하여 반환된 URL(두 번째 두 줄)에서 만화 이미지를 로드합니다.

종속성 { 구현("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0") 구현("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0") 구현("com.github.bumptech.glide:glide:4.11.0") annotationProcessor("com.github.bumptech.glide:compiler:4.11.0") }
기본적으로 새로 생성된 프로젝트에는 MainActivity 및 해당 레이아웃 파일인 activity_main.xml
이 포함되어야 합니다. 여기에 제목을 위한 TextView
하나와 만화 자체에 대한 ImageView 하나를 추가해 보겠습니다.
<?xml 버전="1.0" 인코딩="utf-8"?> <LinearLayout xmlns:andro 기계적 인조 인간: android:layout_width="match_parent" android:layout_height="match_parent" 안드로이드:중력="중심" android:orientation="수직"> <텍스트뷰 기계적 인조 인간: android:layout_width="wrap_content" android:layout_height="wrap_content"/> <이미지 보기 기계적 인조 인간: android:layout_width="match_parent" android:layout_height="wrap_content"/> </리니어레이아웃>
그런 다음 앱 상태에 대한 일부 표현이 필요합니다 . 새 만화를 로드하거나 표시하거나 로드하는 동안 오류가 발생할 수 있습니다.
봉인된 클래스 상태 { 객체 로딩 : State() 클래스 Success(val 결과: ComicModel) : State() 개체 오류: State() }
이제 이전에 만든 비즈니스 로직을 사용하여 최소한의 ViewModel을 추가해 보겠습니다. 모든 클래스를 가져올 수 있습니다. MutableLiveData
는 관찰 가능한 필드입니다. 뷰는 변경 사항을 관찰하고 그에 따라 자체 업데이트합니다. viewModelScope
는 viewmodel의 수명 주기와 연결된 코루틴 범위입니다. 앱이 닫힌 경우 보류 중인 작업을 자동으로 취소합니다.
클래스 MainViewModel : ViewModel() { 개인 값 getLatestComicUseCase = GetLatestComicUseCase(XkcdApi()) val 만화 = MutableLiveData<State<ComicModel>>() 재미있는 fetchComic() { 뷰모델스코프.런치 { comic.value = State.Loading() runCatching { getLatestComicUseCase.run() } .onSuccess { 만화.값 = State.Success(it) } .onFailure { 만화.값 = State.Error() } } } }
마지막으로 모든 것을 연결하는 MainActivity입니다.
클래스 MainActivity : AppCompatActivity(R.layout.activity_main) { private val viewModel: MainViewModel by lazy { ViewModelProvider(this).get(MainViewModel::class.java) } 재정의 재미 onCreate(savedInstanceState: Bundle?) { super.onCreate(저장된 인스턴스 상태) viewModel.comic.observe(이) { 언제(그것) { 상태입니다.로드 중 -> { findViewById<TextView>(R.id.titleLabel).text = "로드 중" } 상태입니다.성공 -> { findViewById<TextView>(R.id.titleLabel).text = it.result.title Glide.with(이) .load(it.result.img) .into(findViewById(R.id.image)) } State.Error입니다 -> { findViewById<TextView>(R.id.titleLabel).text = "오류" } } } viewModel.fetchComic() } }
이제 Android 앱이 준비되었습니다!
iOS용 간단한 UI
위의 모든 작업은 Android Studio에서 수행되었으므로 이 부분에서는 Xcode로 전환하여 더 편리하게 만들어 보겠습니다. 이렇게 하려면 Xcode를 열고 iosApp 디렉토리를 선택하기 만 하면 됩니다. 여기에는 사전 구성된 Xcode 프로젝트가 포함되어 있습니다. 기본적으로 이 프로젝트는 GUI용 SwiftUI를 사용하므로 단순성을 위해 그대로 사용하겠습니다.
가장 먼저 할 일은 만화 데이터를 가져오기 위한 기본 로직을 만드는 것입니다. 이전과 마찬가지로 상태를 나타내는 것이 필요합니다.
열거 상태 { 케이스 로딩 케이스 성공(ComicModel) 케이스 오류 }
다음으로 한번 더 ViewModel을 준비해보자
클래스 ViewModel: ObservableObject { getLatesteComicUseCase = GetLatestComicUseCase(xkcdApi: XkcdApi()) @Published var comic = State.loading 초기화() { self.comic = .loading getLatestComicUseCase.run { fetchedComic, 오류 fetchedComic != nil { self.comic = .success(fetchedComic!) } 또 다른 { self.comic = .error } } } }
그리고 마지막으로 뷰입니다.
참고: 단순성을 위해 Android에서 Glide를 사용한 것처럼 SwiftUI 구성 요소 RemoteImage를 사용하여 이미지를 표시했습니다.
struct ContentView: 보기 { @ObservedObject private(set) var viewModel: ViewModel var 본문: 일부 보기 { 코믹뷰() } -- private func comicView() -> 일부 보기 { viewModel.comic 전환 { 케이스 .로딩: 반환 AnyView(텍스트("로드 중")) 사례 .result(만화로 하자): 반환 AnyView(VStack { 텍스트(comic.title) RemoteImage(url: comic.img) }) 경우 .오류: 반환 AnyView(텍스트("오류")) } } }
그게 다야, iOS 앱도 준비되었습니다!
요약
마지막으로 제목의 질문에 답하려면 Kotlin Multiplatform이 크로스 플랫폼 개발의 미래입니까? – 그것은 모두 필요에 달려 있습니다. 두 모바일 플랫폼을 위한 작고 동일한 앱을 동시에 만들고자 한다면 두 플랫폼의 개발에 필요한 지식이 필요하기 때문에 아마 그렇지 않을 것입니다.
전문가와 함께 다음 앱 개발
견적그러나 이미 Android 및 iOS 개발자 팀이 있고 최고의 사용자 경험을 제공하려는 경우 개발 시간을 크게 단축할 수 있습니다 . 제공된 예제와 같이 공유 모듈 덕분에 응용 프로그램 로직은 한 번만 구현되었으며 사용자 인터페이스는 완전히 플랫폼별 방식으로 생성되었습니다. 시도하지 않으시겠습니까? 보시다시피 시작하기 쉽습니다.
비즈니스 관점에서 크로스 플랫폼 개발에 대해 궁금하십니까? 크로스 플랫폼 개발의 장점에 대한 기사를 확인하십시오.