저번 글에서 언급했던 moko-resources 를 이용한 폰트 적용을 다뤄보도록 하겠다.
CMP 로 개발 중인 토이 프로젝트 앱에서 사용중인 버튼의 텍스트에 폰트를 적용하기 위해 moko-resources 라이브러리를 프로젝트에 주입해보았다.
공식 문서를 참고해서 차근차근 라이브러리의 사용을 위한 환경 설정을 해주면 다음과 같다.(strings 의 경우 국제화를 위한 추가 설정이 필요한데 그런 부분은 신경써주지 않아도 되서 비교적 간단하다.)
[versions]
moko-resources = "0.23.0"
[libraries]
moko-resources-generator = { group = "dev.icerock.moko", name = "resources-generator", version.ref = "moko-resources" }
moko-resource = { group = "dev.icerock.moko", name = "resources", version.ref = "moko-resources" }
moko-resource-compose = { group = "dev.icerock.moko", name = "resources-compose", version.ref = "moko-resources" }
[bundles]
moko-resources = ["moko-resource", "moko-resource-compose"]
// 추가
buildscript {
dependencies {
classpath(libs.moko.resources.generator)
}
}
moko-resources 레포에서는 이렇게 추가하라고 나와있을텐데, 위의 설정을 제외한 나머지 설정들은 settings.gradle.kts 에 이미 존재하기에 위에 설정만 추가해줘도 성공적으로 빌드가 가능했다.
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsCompose)
id ("dev.icerock.mobile.multiplatform-resources") // 추가
}
...
android {
namespace = ""
compileSdk = libs.versions.android.compileSdk.get().toInt()
sourceSets {
named("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
res.srcDirs("src/androidMain/res")
resources.srcDirs("src/commonMain/resources")
java.srcDirs("build/generated/moko/androidMain/src")
}
}
defaultConfig {
applicationId = ""
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
debugImplementation(libs.compose.ui.tooling)
implementation(libs.coil.compose)
commonMainApi(libs.bundles.moko.resources) // 추가
}
}
multiplatformResources {
multiplatformResourcesPackage = "${packageName}" <- 프로젝트의 packageName 추가
}
multiplatformResourcesClassName = "SharedRes" // optional, default MR
을 추가해줄 수 있는데, MR(moko-resources의 약자)도 나쁘지 않아, 그대로 두었다.
일단 이렇게 하면 Android 에서 해줘야할 setting 은 끝이어서, font 파일을 추가해주었다.
다음과 같이 ComposeApp 모듈내에 commonMain 내부를 보면 resources 패키지가 존재하는데 그 내부에 MR/fonts 패키지를 생성한 후에 사용할 font 파일들(ttf, otf)을 추가하면 된다.
그렇게 하면 이제 자동으로 아래와 같은 코드가 생성되는데
composable 함수내에서는 다음과 같이 Pretendard-Semibold의 중간의 - 를 기준으로 분리되어 Pretendard.semiBold 이런식으로 접근이 가능해진다.(컨벤션이 그러하니, font 파일명을 접근하기 용이하게 바꾸어주었다.)
Text(
text = text,
modifier = Modifier.padding(horizontal = 16.dp),
color = White,
fontFamily = fontFamilyResource(MR.fonts.Pretendard.semiBold), // <-
fontWeight = FontWeight.SemiBold,
fontStyle = FontStyle.Normal,
fontSize = 18.sp,
lineHeight = 27.sp,
)
이제 빌드를 해보려고 하는데...
위에 사진에서의 생성된 expect object 인 MR에서 에러가 발생하였다.
공식문서에 나와 있는 대로 모든 세팅을 해줬는데...
하면서 이제 레포에 등록된 이슈를 뒤져보았고, 같은 문제를 겪는 분들을 많이 찾아볼 수 있었다.
https://github.com/icerockdev/moko-resources/issues/531
이슈에 달린 덧글들을 읽어보며, agp 버전을 최신 버전인 8.2.0++ 로 올려보았으나, 에러가 해결되진 않았고
https://github.com/icerockdev/moko-resources/issues/510#issuecomment-1619141070
해당 comment 나에겐 유효했다.
comment 에서 알려준 방법대로 commonMain 에 다음과 같이 추가 세팅을 적용해주었다.
kotlin {
sourceSets {
val desktopMain by getting
// 추가
val iosX64Main by getting {
resources.srcDirs("build/generated/moko/iosX64Main/src")
}
// 추가
val iosArm64Main by getting {
resources.srcDirs("build/generated/moko/iosArm64Main/src")
}
// 추가
val iosSimulatorArm64Main by getting {
resources.srcDirs("build/generated/moko/iosSimulatorArm64Main/src")
}
...
}
}
android {
namespace = "org.moneyking.imagepicker"
compileSdk = libs.versions.android.compileSdk.get().toInt()
sourceSets {
named("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
res.srcDirs("src/androidMain/res")
resources.srcDirs("src/commonMain/resources")
// 추가
java.srcDirs("build/generated/moko/androidMain/src")
}
}
}
이후 빌드를 해보았고, 성공적으로 Android 에서 폰트가 적용된 것을 확인할 수 있었다.
폰트 적용 전 | 폰트 적용 후 |
---|---|
iOS 에서도 마찬가지로 font 를 적용하기 위해선 iOS 프레임워크에서의 사용을 위한 복사 작업이 필요로 하기에, 아래와 같은 script 를 Xcode로 실행한 프로젝트 내에 추가해줘야 한다.
Xcode 의 custom script 관련한 자세한 설명은 아래 글을 참고하시면 좋습니다.
Xcode custom script 시작하기
jetbrains 공식 홈페이지의 compose-multiplatform project wizard 로 만든 project 의 경우 pods 폴더가 iOSApp 모듈내에 존재하지 않아, 아래의 case(Without org.jetbrains.kotlin.native.cocoapods) 에 해당 하는 것 같다.(위의 case 로도 빌드가 되는지 확인해보기 위해 복붙을 해보았는데, resource 복사 작업이 제대로 실행되지 않는지, .otf 파일이 존재하지 않는다는 에러를 뱉으면서 build 가 되지 않았다.)
참고로 KMP 프로젝트를 생성할 때 iOS Framework Distribution 탭에서 cocoapods dependency manager 를 골라 프로젝트를 생성할 경우 위와 같이 Pods 패키지가 존재하는 것을 확인할 수 있었다.
따라서 아래의 script 를 복붙해줘야 하는데 어디에??? Xcode 넘 어렵...
수소문 끝에 복붙할 위치를 알아낼 수 있었고
좌측 하단의 TARGETS -> iosApp 을 클릭한 후에, 우측 상단에 Build Phase 로 진입한 후에, 좌측 상단에 + 버튼을 눌러서 [New Run script Phase] 를 클릭해준다.
최하단에 생성된 Run script에(최하단이 아니라면, 최하단으로 옮겨주기)
# Type a script or drag a script file from your workspace to insert its path.
"$SRCROOT/../gradlew" -p "$SRCROOT/../" :composeApp:copyFrameworkResourcesToApp \
-Pmoko.resources.PLATFORM_NAME="$PLATFORM_NAME" \
-Pmoko.resources.CONFIGURATION="$CONFIGURATION" \
-Pmoko.resources.ARCHS="$ARCHS" \
-Pmoko.resources.BUILT_PRODUCTS_DIR="$BUILT_PRODUCTS_DIR" \
-Pmoko.resources.CONTENTS_FOLDER_PATH="$CONTENTS_FOLDER_PATH"
아래의 command 를 입력해주되, :composeApp 이라고 써있는 부분은 프로젝트마다 다르기에 본인의 project 에 맞게 변경해줘야한다.(:yourframeworkproject)
상대 경로가 위와 다를 수 도 있으므로 유의...(머리 아파)
마찬가지로 jetbrains 공식 홈페이지의 compose-multiplatform project wizard 로 만든 project 의 경우, 위와 같이 그대로 작성해주면 된다.
이제 빌드를 해주면 다행히 iOS 에뮬레이터에서도 정상적으로 폰트가 적용되어, 실행되는 것을 확인할 수 있었다.
moko-resources 를 사용할 수 있는 환경 설정을 구축하는게 성공하였으므로, 토이 프로젝트를 고도화하면서 font 외에도 strings(국제화 분기 포함), images 등도 추가해보도록 해야겠다.
라이브러리의 이슈를 해결하고, Xcode Custom Script 작성하는 곳에서 에러를 해결하느라고 하루를 써버렸다...
뭐든 처음 할 때는 어려운게 당연한거다.
한번 해보면 할만 하다. 계속해서 성공 경험을 늘려가보는 것으로!
moko-resources 에 대한 환경 설정을 완료한 관계로 다음 글에서는 국제화(i18n) 까지 고려해야할 String Resources 관련 글을 적어보도록 하겠다.
참고)
https://github.com/icerockdev/moko-resources
https://github.com/icerockdev/moko-resources/issues/531
https://github.com/icerockdev/moko-resources/issues/510#issuecomment-1619141070
https://velog.io/@jmseb3/kmm%EC%97%90%EC%84%9C-resource-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0
https://medium.com/@boobalaninfo/article-1-compose-multiplatform-moko-resource-integration-dbccbf19aab7
https://life-shelter.tistory.com/240
https://velog.io/@yeahg_dev/Xcode-custom-script-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0
https://medium.com/icerock/moko-resources-0-21-with-compose-multiplatform-support-462d8b11116b
https://www.jetbrains.com/help/kotlin-multiplatform-dev/whats-new-compose-1-6-0.html#improved-resources-api-all-platforms
-> 1.6.0 패치 이후로 이제는 moko-resources 를 사용하지 않아도 resources 들을 플랫폼간에 공유할 수 있게 되었다! moko-resources ㅂㅂ2