[ KMP / CMP ] Compose Multiplatform(Wasm)에서 한글 깨짐 현상 방지하기 - 커스텀 폰트 적용

곽의진·2024년 11월 29일
0
post-thumbnail

안녕하세요 오늘은 Compose Multiplatform으로 간단한 샘플앱을 만들던 도중 한글이 깨지는 상황이 발생하여 이를 어떻게 해결했는지를 간단하게 남겨보고자 포스팅을 시작합니다.
관련 Repository: https://github.com/kez-lab/KotlinRPCSample

서론

저는 간단하게 퀴즈 앱을 만들어보고자 아래와 같이 Title, Button 형태의 Screen을 제작하게 되었습니다.

이 과정에서 한글이 위 사진처럼 X박스의 형태로 깨진다는 것을 확인하였고, 아직 알파버전인 Wasm이기에 Wasm자체에 문제라는 것을 직감하고 해결방법을 찾아나섰습니다.

근본적인 이슈의 이유는 잘 모르겠지만, 한글을 지원하는 폰트가 아닌 경우에 한글이 깨진다는 것을 알게되었으며 이를 통해서 커스텀 폰트를 적용한다면 한글 깨짐 에러를 잡을 수 있다는 것을 확인했습니다.

원인 파악 시 참고한 자료

참고 자료는 유광무님과 김만두님의 블로그를 참고하였습니다.
https://holykisa.tistory.com/117
https://kimmandooo.tistory.com/172

해결 방법

해결 방법은 Compose Multiplatform 1.7.0이 나오게 되면서 굉장히 간단해졌습니다.

1. 기본 준비

Compose Multiplatform 프로젝트에서 커스텀 폰트를 사용하려면 compose.components.resources 라이브러리가 필요합니다.

composeApp 모듈의 build.gradle.kts 파일에 아래 종속성을 추가합니다.

commonMain.dependencies {
    implementation(compose.components.resources)
}

2. *.otf, ttf 파일 넣기

아래 사진처럼 composeApp의 commonMain 모듈 내에서 composeResources/font라는 디렉터리 내에 폰트 파일을 넣습니다.
저는 한글을 지원하는 폰트인 IBMPlexSansKR 를 사용했습니다! (Google Font에서 다운받았습니다.)

3. build 후 Res Class 참조

build 를 진행하면 composeApp 모듈 내에 generated/. . . /commonMainResourceAccessors 내에 Font0... 라는 클래스 파일이 생성 된 것을 볼 수 있습니다.

해당 클래스 내부를 확인해보면 IBMPlexSansKR_Medium 라는 변수가 생성된 것을 볼 수 있는데요. 해당 변수를 참조하여 Font객체를 생성할 수 있습니다.

private object CommonMainFont0 {
  public val IBMPlexSansKR_Medium: FontResource by 
      lazy { init_IBMPlexSansKR_Medium() }
}

@InternalResourceApi
internal fun _collectCommonMainFont0Resources(map: MutableMap<String, FontResource>) {
  map.put("IBMPlexSansKR_Medium", CommonMainFont0.IBMPlexSansKR_Medium)
}

internal val Res.font.IBMPlexSansKR_Medium: FontResource
  get() = CommonMainFont0.IBMPlexSansKR_Medium

private fun init_IBMPlexSansKR_Medium(): FontResource =
    org.jetbrains.compose.resources.FontResource(
  "font:IBMPlexSansKR_Medium",
    setOf(
      org.jetbrains.compose.resources.ResourceItem(setOf(),
    "composeResources/kotlinrpcsample.composeapp.generated.resources/font/IBMPlexSansKR-Medium.ttf", -1, -1),
    )
)

4. FontFamily 정의 및 Theme에 적용

Compose Multiplatform에서는 Res.font를 통해 폰트를 로드할 수 있으며, 이를 FontFamily로 변환하여 사용할 수 있습니다.

여기서 주의사항은 꼭 Font객체에 대한 import를 org.jetbrains.compose.resources.Font 로 참조해야합니다.

만약 androidx.compose.ui.text.font.Font 로 참조하게 된다면 FontResource객체를 인자 값으로 활용하지 못하기 때문에 Res.font를 활용할 수 없습니다.


import androidx.compose.material.MaterialTheme
import androidx.compose.material.Typography
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.font.FontFamily
import kotlinrpcsample.composeapp.generated.resources.IBMPlexSansKR_Medium
import kotlinrpcsample.composeapp.generated.resources.Res
import org.jetbrains.compose.resources.Font

@Composable
fun QuizTheme(content: @Composable () -> Unit) {
    val font = Font(Res.font.IBMPlexSansKR_Medium)
    MaterialTheme(
        typography = Typography(
            FontFamily(font)
        ),
        content = content
    )
}

저는 QuizTheme라는 새로운 테마 Composable을 적용하여 Typography의 FontFamily로 IBMPlexSansKR_Medium을 적용한 것을 알 수 있습니다.

결과

결과물을 보시면 아시다시피 한글이 굉장히 이쁘게 나오는 것을 알 수 있습니다!

오늘도 역시 CMP 덕분에 다사다난한 하루를 보내게 되었지만 매번 발전해나가는 Jetbrains 덕분에 다행히 발뻗고 잘 수 있을 것 같네요 하핳 감사합니다!

참고 자료

https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-resources-usage.html#fonts

profile
Android Developer

0개의 댓글