-
Kotlin multiplatform 프로젝트를 생성해보자프로그래밍/Kotlin 2020. 9. 1. 20:20반응형
이 글은 영어 블로그 글을 번역하고 추가 조사한 글입니다.
www.bugsnag.com/blog/kotlin-multiplatform
blog.mindorks.com/getting-started-with-kotlin-multi-platform
코틀린 멀티플랫폼이란?
일반적으로 앱을 만들 때, iOS나 Android로 만든다. 그런데 디자인 변경이 있을 수 있지만, 코어 로직에 대해서는 거의 같을 것이다. 두 개의 앱을 작성하기 위해서 코틀린 멀티플랫폼은 동일한 비지니스 로직을 공유하게 하고 서로 다른 플랫폼에 대해 앱의 빌드를 제공한다.
코틀린 멀티플랫폼은 개발자가 코틀린 언어로 개발할 수 있게 한다. 또한 이는 비지니스 로직을 처리하고 개발자는 각 플랫폼의 UI만 신경쓰면 된다고 한다. 즉, 코틀린 멀티플랫폼은 서로 다른 플랫폼 사이의 코드를 공유하는 방법 중 하나이다.
결론부터 말하면 비지니스로직만 코틀린으로 공유 가능하고 Android, iOS 의 UI 코드는 각 플랫폼 코드를 사용해야한다.
코틀린 멀티플램폼은 어떻게 동작하는가?
한 번 상상해볼 수 있다. Kotlin Shared 코드를 작성하고, 이 코드가 컴파일되는데 자동적으로 플랫폼에 대한 코드로 컴파일되는 것이다. 예를 들면, JVM 위에서 코드가 컴파일되면, 그 컴파일된 코드는 Android나 JVM 플랫폼 위에서 동작하는 것이다.
플랫폼들 간에 코드를 어떻게 공유할 수 있는지?
만약에 플랫폼별로 코드를 구현하려면 아래과 같이 분기처리를 해야할 것이다.
if(platform == "iOS"){ }else if(platform =="android") { }
하지만 코틀린에서는 expect/actual 키워드를 제공한다. 예를 들면, 공통으로 사용하는 모듈에서 expect로 선언해놓고
expect fun platformName(): String object Sample { fun sayPlatformName(): String = platformName() }
각각 android, iOS 패키지에서
import android.os.Build actual fun platformName(): String = "Android ${Build.VERSION.SDK_INT}"
와 같이 actual 키워드를 사용해서 값을 분기처리할 수 있다.
platformName()를 호출하는 android, iOS 코드에서는 플랫폼 코드로 선언된 값을 받아올 수 있을 것이다.
override fun onCreate(savedInstanceState: Bundle?) { .... text.text = Sample.sayPlatformName() }
Android 28이 출력됐다.
어떻게 이게 가능할까?
- 일단 expect 키워드는 함수 이름이나 클래스 앞에 선언이 가능하다. 플랫폼별로 expect되는 함수나 클래스에 선언한다.
- actual 키워드는 플랫폼 별로 common의 코드에서 expect 로 선언한 동일한 함수나 클래스 이름으로 선언한다. 매칭되는 expect 정의에 실제 구현이 필요하다.
- expect로 정의되었던 모든 클래스나 함수는 플랫폼별로 actual 정의를 가져야한다.
- expect 함수나 클래스는 구현되지 않는다.
예제
Kotlin Multiplatform Projects are an experimental feature.
코틀린 멀티플랫폼은 아직 시험버전이다.(2020.09.01 기준) 빌드할 때 인내심을 요한다. 하나하나 천천히 해결해보자!
Android Studio (version >= 4.0) 에서 Preferences > Plugin > "Kotlin Multiplatform Mobile" 을 검색해서 plugin을 설치한다.
이 플러그인을 사용하면 깔끔하게 common, androidApp, iosApp 모듈이 자동으로 생성된다고 한다. (하지만 버그가 있다.)
Android Studio에서 새 프로젝트를 생성할 때, KMM Application으로 생성한다.
프로젝트 이름을 변경한 뒤에 Next 버튼을 누르는데, 이 Next 버튼이 뜨지 않는 버그가 있다. (youtrack.jetbrains.com/issue/KT-41559)
제일 중요한 module 생성이 불가능하다.
이 플러그인을 사용하면 Andoird 와 iOS Application을 IDE를 왔다 갔다할 필요 없이 Android studio에서 바로 빌드할 수 있다.
일단은 버그가 수정될 때까지 기다려보고 수동으로 KMM 프로젝트를 생성해보자.
코틀린 멀티플랫폼 프로젝트는 어떻게 시작하나?
시작하기 위해, IntelliJ CE(Community Edition) Version을 설치한다.
https://www.jetbrains.com/idea/download/#section=mac
Setup 01. Android/iOS 모바일을 선택한다.
Step 02. 적절한 JDK 버전을 설정한 뒤 다음을 누른다.
Step 03. 프로젝트 경로 설정 후 종료를 누른다.
이제 아래 에러를 볼 수 있다.
local.properties 파일에서 SDK 경로를 설정해준다. (없다면 생성한다.)
sdk.dir=/Users/himanshusingh/Library/Android/sdk
다음과 같이 app, iosApp 의 모듈이 생성되었다.
root project의 build.gradle의 버전을 3.4.2 이상으로 수정해준다. (gradle wrapper 커맨드가 동작하지 않는다.)
classpath 'com.android.tools.build:gradle:3.4.2'
Step 04. app 모듈은 iOS와 Android 각 플랫폼에서 공통적으로 사용될 비지니스로직이 추가될 모듈이다. 블로그에서는 이름을 변경하라고 하지만... 필자가 변경을 해보니 string으로까지 선언된 iOS 파일들을 변경하기에는 한계가 있다. 빌드가 잘 되지 않으므로 app 이라는 이름을 그대로 사용한다.
이제, Android application 부터 만들어보자. 이 프로젝트를 Android studio에서 열어준다.
그리고 Android Studio로 Empty Activity를 갖는 새 프로젝트를 만든 다음에 androidApp으로 복사 붙여넣기 추가해준다.
코틀린 멀티플랫폼 프로젝트에 androidApp이 추가되었다.
settings.gradle 에 추가한 androidApp 모듈을 선언해준다.
include ':app', ':androidApp'
하지만, gradle sync 가 동작하지 않을 것이다.
Plugin with id 'kotlin-android' not found.
root project의 build.gradle 에 kotlin gradle plugin 을 추가한다.
buildscript { ext.kotlin_version = "1.3.72" repositories { google() mavenCentral() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" } } repositories { google() mavenCentral() jcenter() } allprojects { repositories { google() mavenCentral() jcenter() } }
Plugin request for plugin already on the classpath must not include a version
app 모듈의 build.gradle 에 multiplatform version을 지워준다.
plugins { id 'org.jetbrains.kotlin.multiplatform' }
androidApp에서 해당 app 모듈을 사용할 수 있도록 변경할 것이다. app 모듈을 library로 변경해주고 dependency와 applicationId도 지워준다.
apply plugin: 'com.android.library' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 30 buildTypes { release { minifyEnabled false } } }
AndroidApp로 어플리케이션 모듈과 common 모듈 코드를 분리했기 때문에 app/src/main/java 의 Activity 클래스도 지워주고 app/src/main/AndroidManifest.xml 의 내용도 지워준다.
<manifest package="sample" />
Step 05. 안드로이드용 멀티플랫폼 코드를 추가할 것이다. app 모듈에 androidMain과 kotlin package를 추가한뒤에 SampleAnd.kt 클래스를 추가한다.
SampleAnd.kt package sample actual class Sample { actual fun checkMe() = 7 } actual object Platform { actual val name: String = "Android" }
Step 06. 이제 안드로이드 앱을 빌드할 것이다. 아래 코드들을 추가해준다.
androidApp/MainActivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.and_textview).text = Platform.name } }
androidApp/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.sample.emptyactivity.MainActivity"> <TextView android:id="@+id/and_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
androidApp/build.gradle
dependencies { implementation project(':app') }
단말에 빌드한다.
이제 iOS 앱을 빌드할 것이다.
iOS 앱을 빌드하기 위해서는 gradlew 스크립트가 필요하다. gradlew 를 생성해준다.
$ gradle wrapper
(gradle 이 없다면 설치 : $brew install gradle)
이제 root에서 아래 커맨드로 디버그 버전의 빌드를 실행한다.
$ xcodebuild -sdk iphonesimulator -arch arm64 -configuration Debug -project "iosApp/iosApp.xcodeproj" -scheme iosApp
github.com/bugsnag/kotlin-multiplatform-example
Xcode를 실행하고 iosApp을 연 뒤에 에뮬레이터에 빌드한 앱을 실행해준다.
app 모듈에 있던 공통 코드가 각 플랫폼 별로 다르게 실행된 것을 확인할 수 있을 것이다.
아직 experimental 이라 빌드도 귀찮고 Android Studio에서 iOS 코드를 작성하는데 어려움이 있지만, 조금만 기다리면 Android Studio에서 한 번에 코딩에서 단말에 실행가능해질 것으로 생각된다.
파일설명
- iosApp/iosApp.xcodeproj/project.pbxproj
- The project.pbxproj contains all of the metadata about your project that Xcode uses to build it; the settings, the file references, configuration, targeted platforms, etc...
- app/build.gradle
- kotlin {}
- kotlin is the top-level block for multiplatform project configuration in the Gradle build script.
- targets in kotlin block.
- Declares a particular target of a project.
- Target is a part of the build responsible for compiling, testing, and packaging.
- The targets of a multiplatform project are described in the corresponding blocks inside kotlin, for example, jvm, android, iosArm64, ...
- task copyFramework {}
- project.pbxproj 에서 호출되며, iosApp을 Xcode 에서 열기 전에 빌드에 필요한 framework 파일들을 copy해주는 task이다.
- kotlin {}
Sample code
github.com/SeungwonLee/kmm_sample
NEXT
The application will retrieve data over the internet from a public API, save it in a local database, and display it in a list in the application.
반응형'프로그래밍 > Kotlin' 카테고리의 다른 글
Coroutine - Cancellation and timeouts (0) 2022.05.09 Making our Android Studio Apps Reactive with UI Components & Redux (0) 2020.11.08 Lessons learnt using Coroutines Flow in the Android Dev Summit 2019 app (0) 2020.01.29 Improve app performance with Kotlin coroutines (0) 2019.11.29 Kotlin by keyword를 이용해 상속대신 Delegation을 해보자 (0) 2019.09.03