상세 컨텐츠

본문 제목

[KMP] 코틀린 멀티 플랫폼 A-Z 프로젝트 생성 및 환경구축

Android 캐기/Kotlin Multiplatform

by Atlas 2023. 9. 18. 18:10

본문

728x90
반응형

시나리오 : 코틀린 멀티 플랫폼(KMP) 초기 환경 설정을 처음부터 시작해보았다.
 
KMP를 사용하기 위해서 젯브레인에서 제공하는 템플릿으로 동작확인을 하다가 템플릿으로 구성되어있는 부분까지 프로젝트 세팅부터 A-Z를 직접 진행해보았다. 
https://github.com/JetBrains/compose-multiplatform-ios-android-template/#readme

 

GitHub - JetBrains/compose-multiplatform-ios-android-template: Compose Multiplatform iOS+Android Application project template

Compose Multiplatform iOS+Android Application project template - GitHub - JetBrains/compose-multiplatform-ios-android-template: Compose Multiplatform iOS+Android Application project template

github.com

 

🧨 No Silver Bullet 🔫  

금방 진행하겠지라고 생각했지만 생각보다 삽질을 많이했던 시간이었다. 
차근차근 하나씩 진행해보자 
 

1. 프로젝트 생성

 

 

 

*참고*

코코아팟 관련해서 프로젝트를 생성한다면 iOS distribution에서 옵션을 CocoaPods dependency manger를 선택하고 프로젝트를 생성하자 ( Regular framework로 한 다음에 설정하면 되지않을까해서 다양한 방법을 시도해봤지만 성공하지 못했다. 성공하신 분들은 HowTo 알려주시면 감사드립니다! ) 

 

 

MainView.kt 파일을 생성해서 AOS, iOS에서 공통적으로 사용할 UI를 만들어보자.
Shared > src > comonMain > kotlin > com.example.myapplication [설정한 패키지] > MainView.kt


 
 

 

2. Shared -> aOS, iOS 공통으로 사용할 화면을 Compose로 작성

2-1. 플러그인 추가

그런데...응? 

Compable을 import 할 수 없다늬... 라이브러리를 추가해서 해결하자. 
 
shared > build.gradle.kts 
 

plugins {
	...
	id("org.jetbrains.compose")
    ...
}

 
commonMain에도 
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material) 를 추가해주자. 

kotlin {
	...
    sourceSets {
            val commonMain by getting {
                dependencies {
                    //put your multiplatform dependencies here
                    implementation(compose.runtime)
                    implementation(compose.foundation)
                    implementation(compose.material)
                }
            }
           ...
        }
    ...
}

 
그리고 한번 sync 를 맞춰주자.
 



compose의 버전을 지정해주자.

plugins {
	...
	id("org.jetbrains.compose") version "1.5.1"
    ...
}

 
다시 한번 sync
 

그냥 되는거 하나 없... ㅠㅠㅠ 
 
gradle.properties에서 아래 속성을 추가한다. 

org.jetbrains.compose.experimental.uikit.enabled=true

 
이제 싱크를 클릭하면 정상적으로 빌드가 된다고 뜬다.

✅ 이제 다시 코드로 돌아가서 보면 import 가 가능하다! 🥳 

 

벌써 하얗게 불태운 이 느낌....

2-2. 공용으로 사용될 화면 개발

간단하게 파라미터로 받아오는 문자열을 보여주는 화면을 만들어보자.

@Composable
fun App(
    sayHello: String,
    modifier: Modifier = Modifier.fillMaxSize()
        .background(Color.White)
){
    Text(text = sayHello)

}

 
2.2.1 shared > src > androidMain > .... > Platform.android.kt 파일에서 안드로이드에서 호출할 Composable을 만들어 준다. 

@Composable
fun MainView()= App(sayHello = getPlatform().name)

전체코드 

import androidx.compose.runtime.Composable

actual fun getPlatformName(): String = "Android"

@Composable
fun MainView()= App(sayHello = getPlatform().name)

 
2.2.2 프로젝트에서 > androidApp > src > main > java > ... > MainActivity.kt에서 MainView() 를 호출하자.
 

 

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    MainView()
                }
            }
        }
    }
}

 
2.2.3 안드로이드 실행
안드로이드부터 실행되는지 먼저 실행시키려고 해보니 레드카펫을 깔아주....아니 에러를 깔아주는 안스... 

 
고지가 멀지 않았다. compileSdk와 targetSdk를 34로 업데이트하고 Gradle 싱크까지하고 실행하니 정상적으로 동작하는걸 확인할 수 있었다. 👍👍👍
 

 
 

3.  iOS 시뮬레이터로 실행해보자.

자 이제 iOS 시뮬레이터에도 적용해보도록 하자.
Composable 어노테이션을 작성할 필요없이 ComposeUIViewController를 이용해서 Composable 화면을 SwfitUI 어플리케이션에 임베딩할 수 있다.

fun MainViewController() = ComposeUIViewController { App(sayHello = getPlatform().name) }

 
프로젝트에서 iosApp > iosApp > iOSApp.swift 파일을 열어서 코드를 작성해 보자.

 
전체코드

import UIKit
import SwiftUI
import shared // 공통으로 사용할 모듈인 shared를 import 해준다

@main
struct iOSApp: App {
	var body: some Scene {
	   WindowGroup {
                ZStack {
                    Color.white.ignoresSafeArea(.all) // status bar color
                    ComposeView()
                        .ignoresSafeArea(.all, edges: .bottom) // Compose has own keyboard handler
                }.preferredColorScheme(.light)
            }
	}
}

//ComposeUIViewController로 생성했기 때문에 UIViewControllerRespentable 프로토콜을 사용하여
//SwiftUI환경에서 사용할 수 있도록 해준다. 
struct ComposeView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        Platform_iosKt.MainViewController()
    }
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

 
configuration 을 iosApp으로 변경하고 Run을 클릭해보자 .
정상적으로 동작되는것을 확인할 수 있다. (안드로이드 구동시킬 때보다 금방 됨 😄)
 

3.1 결과 화면

 
 

4. 이벤트 구현 

마지막으로 템플릿에서 확인할 수있는 버튼 클릭 이벤트를 구현해보자.
 
구현할 기능
1.버튼을 클릭
2.이미지가 애니메이션과 함께 짜-잔 등장
 

4.1 리소스를 관리할 폴더를 만든다.

 

4.2 implementation 추가 

shared > build.gradle.kts 에서 아래의 코드 2줄을 넣어주자.
 

// 컴포즈에서 제공하는 resources라이브러리는 아직 실험적인 단계임으로 해당 어노테이션과 함께 사용해야한다.
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.components.resources)

 
전체코드

kotlin {
	...
    sourceSets {
            val commonMain by getting {
                dependencies {
                    //put your multiplatform dependencies here
                    ...
                   @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
                   implementation(compose.components.resources)
                    ...
                }
            }
           ...
        }
    ...
}

 
그리고 android 안에 sourceSets 경로를 추가해준다. 이 경로를 추가해주고 sync 를 맞추게 되면 우리가 생성해뒀던 resources 폴더가 resources root가 되어있다는걸 확인할 수 있다. 

android {
	...
    sourceSets["main"].resources.srcDirs("src/commonMain/resources")
    ...
}

 
 

4.3 xml 파일 추가

이 파일은 example 에서 제공하는 파일을 동일하게 복붙하여 사용하였다. 
 

 

START

@Composable
fun App(
    sayHello: String,
    modifier: Modifier = Modifier.fillMaxSize()
        .background(Color.White)

){
// 코드작성 

}

 
간단하게 버튼과 이미지를 컬럼으로 감싸서 구현했다. 

  Column (modifier = modifier){
        Button(
            onClick = {
			// 버튼 클릭시 
        }) {
            Text(sayHello)
        }
        
        Image(
            painterResource("compose-multiplatform.xml"),
            null
        )

    }

 
이렇게 실행하게 되면 아래와 같이 버튼과 이미지가 나온다. 

 
버튼 클릭 시 애니메이션이 보였다 없어졌다 할 수 있도록 showImage 변수로 컨트롤 해보자. 

  var showImage by remember { mutableStateOf(false) }
    Button(
        onClick = {
        showImage = !showImage
    }) {
        Text(sayHello)
    }
  Column (modifier = modifier){
    ...
        AnimatedVisibility(showImage) {
            Image(
                painterResource("compose-multiplatform.xml"),
                null
            )
        ...
    }

 

FINAL

@OptIn(ExperimentalResourceApi::class)
@Composable
fun App(
    sayHello: String,
    modifier: Modifier = Modifier.fillMaxSize()
        .background(Color.White)

){
    Column (modifier = modifier){
        var showImage by remember { mutableStateOf(false) }
        Button(
            onClick = {
            showImage = !showImage
        }) {
            Text(sayHello)
        }

        AnimatedVisibility(showImage) {
            Image(
                painterResource("compose-multiplatform.xml"),
                null
            )
        }
    }
}

 
 

5. 결과화면

두 플랫폼에서 버튼 클릭에 따라서 이미지가 애니메이션과 함께 동작하는 것을 확인할 수 있었다. 

 
 
 
 
 
마무리 
- 템플릿을 사용해서 바로 개발을 진행해도 좋지만 이렇게 A-Z 환경설정부터 간단한 기능 테스트를 해보니 훨씬 이해하기 좋았다. 

깃 주소 :

https://github.com/PotatoArtie/Potato-iOS/tree/master/Labs/Playground/MyApplication

 

반응형

관련글 더보기

댓글 영역