Android, IOS,웹 애시당초 같은 로직을 언어마다 따로 만든다는게 잘못된거 아닌가? 그냥 C++ 하나 정도면 사람이 프로그램하는게 전혀 문제가 없는데, 뭘또 계속 만드는지...여튼 이런 환경에서 Kotlin 이 어느정도 답을 제시하고 있다.
Kotlin MPP 프로젝트는 IntelliJ에서 해도 되고, 익숙한 Android Studio Plugin (Kotlin Multiplatform Mobile) 을 설치해서 해도 된다.
AndroidStudio 를 이용하면 바로 IOS 도 실행 해볼수 있다.(안되더라)
1. 안드로이드스튜이도에서 신규작성을 할때, 맨마지막에 있는 KMM Application을 선택하자.
2.앱이름과 등등 이름을 입력하고 생성하자. 관련 라이브러리 받으라 더럽게 오래 걸린다.
https://github.com/sugoigroup/kotlin_mpp_example/commit/9ce2f2fd791270c86eeae6265837a9b49eb473f5
3.shared 의 build.gradle.kts를 보면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | kotlin { android() ios { //ios가 sourceSet에서는 iosMain으로 지정 binaries { framework { baseName = "shared" // ios프로젝트를 xcode에서 열어보면 shared란 framework 이름으로 추가되어있다. } } } sourceSets { val iosMain by getting } } |
3. Hello 공용모듈을 분석해보자
commonMain 아래에 있는것들이 말그대로 공용함수가 있는곳이다. 프로젝트 작성과 함께Greeting.kt, Platform.kt 가 있다.
kotlin 에서는
expect : 이 키워드를 붙인 클래스나 함수는 각 플랫폼에서 동일하게 사용될것 같은 (마치 인터페이스같은) 것이라는 의미이다.
actual : 앞서 expect로 외부에서 불러내면 , 각 플랫폼에따라서 상황에 맞는 동작을 해야되는데 actual 키워드를 클래스나 함수 앞에 붙여준다. 따라서 expect 가 붙어있는 것들은 각 플랫폼(iosMain, AndroidMain, jsMain등)에 aspect로 쌍을 이뤄서 써줘야한다.
그래서
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | --shared/commonMain/.../Greeting.kr
//Greeting 클래스는 외부의 각플랫폼에서 불러낼수 있다. 단, 어떤 플랫폼에서 불러내든 동일한 처리와 결과같을 가지게 된다.
class Greeting {
fun greeting(): String {
return "Hello, ${Platform().platform}!" // Platform().platform 이라는 expect(실제의 구현체가 있을지 없을지 모르는 예상의) 키워드가 붙은 클래스를 불러냈다.
}
}
--shared/commonMain/.../Platform.kr
// expect가 붙은 Platform 클래스는 platform(문자형변수)를 가지고있고, 외부에서 Greeing.greeting()을 불러실행할떄, Platform().platform 형식으로 값을 가져다가 출력한다. 이곳에는 단지 expect만 지정했기때문에 실제 구현체는 공용모듈안의 각 플랫폼에서 진행하자.
expect class Platform() {
val platform: String
}
--shared/androidMain/Platform.kr
// exctual라인옆에 <E> 가 보인다. expect에 대응하는 안드로이드플랫폼용 actual을 구현하자.
// actual이 조금 지저분하다. 여튼 안드로이드는 SDK_INT로 버젼을 확인하니 해당 코드를 넣어주었다
actual class Platform actual constructor() {
actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
--shared/iosMain/Platform.kr
// ios코드가 재밌다. UIDevice클래스(랩퍼겠지?)를 이용하여 버젼을 출력하는 코드를 넣었다..
actual class Platform actual constructor() {
actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
이로써 공통 모듈은 준비가 되었다.
이제 각 플랫폼에서 공통모듈을 불러내서 Greeting함수만 실행하면 각 플랫폼의OS버젼이 출력된다.
--androidApp/.../MainActivity.kt
안드로이드의 gradle.build 에 공용모듈의 의존성을 추가(implementation(project(":shared"))한다음소스에서 클래스와 함수 불러내면 된다.
호출:Greeting().greeting();
결과:"Hello, Android 28!"
결국 shared모듈을 의존성에 추가하면, 공통 Greeting클래스라 expect된 Platform().platform을 호출하고 androidMain플랫폼에 actual하여 적어준대로 "Android ${android.os.Build.VERSION.SDK_INT}"를 출력하게 된다.
--iosApp/.../ContentView.swift
안드로이드와 비슷하게, ios프로젝트 내부에 공용모듈인 shared.framework을 추가되었고, swift언어로 클래스와 함수를 불러냈다.
호출:Greeting().greeting();
결과:"Hello, ios ....!"
마찬가지로, expect된 Platform().platform을 iosMain 에서 "UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion" 식으로 출력했다. |
4. 그럼 나도 추가해보자. 일단 멀티플랫폼에서 사용할만한게...환전?! 환전예상가격을 조회하는 어플을 만든다 할때, 미국달러를 나라별 환전금액으로 변환해주는 로직을 안드로이드와 아이폰 양쪽에서 만들필요없이 공통로직에서 처리하면 좋을것 같다.
https://github.com/sugoigroup/kotlin_mpp_example/commit/5c7956fa68dd2fb6dc93ad278d99804bb5eae352
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | --commonMain/.../ExchangeMoney.kt 외부에서 사용할 클래스와 함수를 expect 정의 expect class ExchangeMoney(baseDollor: Double) { fun toYen(): Double fun toWon(): Double } --commonMain/.../ExchangeMoneyCalcurator.kt 각 플랫폼에서 expect함수를 통해 actual 선언된 함수를 이용할때, 플랫폼에 상관없이 똑같이 처리할 로직을 넣음 class ExchangeMoneyCalcurator(val baseDollor: Double, val country: String) { fun calc(): Double { val rate: Double = when (country) { "YEN" -> 0.91 "WON" -> 0.95 else -> 1.0 } return baseDollor * rate } } --androidMain/.../ExchangeMoney.kt expect 함수들을 actual로 구현. 단 공통로직은 ExchangeMonetCalcurator를 호출하여 구현 actual class ExchangeMoney actual actual class ExchangeMoney actual constructor(val baseDollor: Double) { actual fun toYen(): Double { return ExchangeMoneyCalcurator(baseDollor, "YEN").calc() } actual fun toWon(): Double { return ExchangeMoneyCalcurator(baseDollor, "WON").calc() } } --iosMain/.../ExchangeMoney.kt actual class ExchangeMoney actual constructor(val baseDollor: Double) { actual fun toYen(): Double { return ExchangeMoneyCalcurator(baseDollor, "YEN").calc() } actual fun toWon(): Double { return ExchangeMoneyCalcurator(baseDollor, "WON").calc() } } --androidApp/.../MainActivity.kt 사용하자 tv.text = ("3 달라는 한국돈으로 " + ExchangeMoney(3.0).toWon()) --iodApp/.../ContentView.swift 사용하자 Text("3 달라는 엔화로" + String(ExchangeMoney(baseDollor: 3.0).toYen())) |
이밖에 멀티플랫폼에서 사용할만한 라이브러리가 있다.
https://github.com/AAkira/Kotlin-Multiplatform-Libraries
GraghQL, SQLDelight, Realm, Koin, LiveData, coroutine, uuid, firebase, moko-widget 등 디비와 통신, 로직에 사용할 만한 라이브러리 들이 있다
0 comments:
댓글 쓰기