๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ’ป/๊ธฐํƒ€

๋‚ด ํ”„๋กœ์ ํŠธ์™€ Now in Android์˜ ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ ์† ํ”„๋กœํผํ‹ฐ๋ช… ๋งž์ถ”๊ธฐ

interfacer_han 2025. 10. 16. 14:39

#1 ๊ฐœ์š”

#1-1 Whatใ†Why

 

Now in Android  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. Now in Android ์•ฑ, Play ์Šคํ† ์–ด์— ์ถœ์‹œ Now in Android๋Š” ์™„์ „ํ•œ

developer.android.com

Now in Android๋Š” Android ์„ค๊ณ„ ๋ฐ ๊ฐœ๋ฐœ ๊ถŒ์žฅ์‚ฌํ•ญ๋Œ€๋กœ ๋งŒ๋“  '๊ณต์‹์ ์ธ ์ƒ˜ํ”Œ' ์•ฑ์ด๋‹ค (์†Œ์Šค ์ฝ”๋“œ). ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ์ค‘, ๋‚ด ํ”„๋กœ์ ํŠธ์˜ ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ ์† ํ”„๋กœํผํ‹ฐ๋ช…๊ณผ Now in Android์˜ ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ ์† ํ”„๋กœํผํ‹ฐ๋ช…์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ–ˆ๋‹ค. ๋‚˜๋Š” Now in Android์˜ ์Šคํƒ€์ผ์„ ๋”ฐ๋ผ๊ฐˆ ๊ฒƒ์ด๋ฏ€๋กœ, ํ”„๋กœํผํ‹ฐ๋ช…๊นŒ์ง€ ๋˜‘๊ฐ™์ด ๋งž์ถ”๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋Ÿฌํ•œ '๋ถˆ์ผ์น˜'๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฒ€์ฆ์šฉ ์ฝ”ํ‹€๋ฆฐ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

 

#1-2 tomlj ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

 

GitHub - tomlj/tomlj: A Java parser for Tom's Obvious, Minimal Language (TOML).

A Java parser for Tom's Obvious, Minimal Language (TOML). - tomlj/tomlj

github.com

"tomlj"๋Š” Java(์™€ Kotlin)์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” TOML ํŒŒ์ผ ํŒŒ์‹ฑ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ๋Š” TOML ํ™•์žฅ์ž๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค (์ฐธ๊ณ : Maven Repository์˜ tomlj ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŽ˜์ด์ง€).

 

#2 ์ฝ”๋“œ

#2-1 ์Šคํฌ๋ฆฝํŠธ

import org.tomlj.Toml
import org.tomlj.TomlTable
import java.nio.file.Paths

data class TomlElement(
    val propertyName: String,
    val group: String?,
    val name: String?,
    val module: String?,
    val id: String?,
    val versionRef: String?
) {
    val identifier: String? = if (group != null && name != null) {
        "$group:$name"
    } else {
        module ?: id
    }

    fun isStrictlyEqualTo(other: TomlElement): Boolean {
        return propertyName == other.propertyName
                && group == other.group
                && name == other.name
                && module == other.module
                && id == other.id
                && versionRef == other.versionRef
    }

    fun makeTomlLine(): String {
        var tomlLine = "$propertyName = { "
        group?.let { tomlLine += "group = \"${it}\", " }
        name?.let { tomlLine += "name = \"${it}\", " }
        module?.let { tomlLine += "module = \"${it}\", " }
        id?.let { tomlLine += "id = \"${it}\", " }
        versionRef?.let { tomlLine += "version.ref = \"${it}\", " }
        tomlLine = tomlLine.removeSuffix(", ") + " }"

        return tomlLine
    }
}

const val MY_PROJECT_VERSION_CATALOG_PATH = "C:\\prj\\AndroidStudioProjects\\my-project-example\\gradle\\libs.versions.toml"
const val NIA_VERSION_CATALOG_PATH = "C:\\prj\\AndroidStudioProjects\\nowinandroid\\gradle\\libs.versions.toml"

fun main() {
    val myList = getTomlElementList(
        versionCatalogPath = MY_PROJECT_VERSION_CATALOG_PATH
    )?.filter { it.identifier != null }

    val niaList = getTomlElementList(
        versionCatalogPath = NIA_VERSION_CATALOG_PATH
    )?.filter { it.identifier != null }

    var result = ""

    myList?.forEach { myElement ->
        val niaElement = niaList?.find { niaElement ->
            (myElement.identifier == niaElement.identifier) and
                    !myElement.isStrictlyEqualTo(niaElement)
        }

        if (niaElement != null) {
            val tomlLine1 = myElement.makeTomlLine()
            val tomlLine2 = niaElement.makeTomlLine()
            result += " [My] $tomlLine1\n[Nia] $tomlLine2\n\n"
        }
    }

    println(result)
}

fun getTomlElementList(versionCatalogPath: String): List<TomlElement>? {
    val tomlPath = Paths.get(versionCatalogPath)
    val toml = Toml.parse(tomlPath)

    val librariesMap = toml.getTable("libraries")?.toMap() ?: emptyMap()
    val pluginsMap = toml.getTable("plugins")?.toMap() ?: emptyMap()
    val list = (librariesMap + pluginsMap).mapNotNull { (key, value) ->
        val isValueTomlTableType = value is TomlTable // TomlTable ํ˜•์œผ๋กœ ํ˜•๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•œ๊ฐ€?
        if (!isValueTomlTableType) {
            return@mapNotNull null
        }

        val group = value.getString("group")
        val name = value.getString("name")
        val module = value.getString("module")
        val id = value.getString("id")
        val versionRef = value.getString("version.ref")

        TomlElement(
            propertyName = key,
            group = group,
            name = name,
            module = module,
            id = id,
            versionRef = versionRef
        )
    }

    return list
}

๋จผ์ € build.gradle์˜ dependencies์— implementation("org.tomlj:tomlj:1.1.0")์™€ ๊ฐ™์€ ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ, import๋ฌธ์„ ์ธ์‹ํ•ด์„œ ์œ„์˜ ์ฝ”๋“œ๊ฐ€ ์ž‘๋™ํ•œ๋‹ค. ์•„๋ฌดํŠผ MY_PROJECT_VERSION_CATALOG_PATH์— ๋‚ด ํ”„๋กœ์ ํŠธ์˜ ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ, NIA_VERSION_CATALOG_PATH์— Now in Android ํ”„๋กœ์ ํŠธ์˜ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ ๊ณ  ์‹คํ–‰์‹œํ‚ค๋ฉด ๋œ๋‹ค.

 

"libraries" ํ…Œ์ด๋ธ”์˜ ํ”„๋กœํผํ‹ฐ๋ช…(key๊ฐ’)์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, version.ref ์— ํ• ๋‹น๋œ ๋ฌธ์ž์—ด์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋„ ๊ฐ์ง€ํ•˜๊ฒŒํ–ˆ๋‹ค. ์ด ๊ฒฝ์šฐ๋Š”, version.ref ์— ํ• ๋‹น๋˜๋Š” "versions" ํ…Œ์ด๋ธ”์˜ ํ”„๋กœํผํ‹ฐ๋ช…(key๊ฐ’)์„ (Now in Android์™€ ๋˜‘๊ฐ™๊ฒŒ) ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.  ๋˜ ํ•œ ์ชฝ์—์„  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ด๋ฆ„์„ "group" ๋ฐ "name"์— ๋‚˜๋ˆ ๋‹ด์•˜๋Š”๋ฐ ๋‹ค๋ฅธ ์ชฝ์—์„  "module"์— ํ†ต์งธ๋กœ ๋‹ด์€ ๊ฒฝ์šฐ ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋„ ๊ฐ์ง€ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

 

#2-2 ์‹คํ–‰ ๊ฒฐ๊ณผ 

 [My] androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
[Nia] androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" }

 [My] androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
[Nia] androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" }

 [My] hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltVersion" }
[Nia] hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }

 [My] androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
[Nia] androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }

 [My] androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
[Nia] androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }

 [My] androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
[Nia] androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }

 [My] androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
[Nia] androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }

 [My] androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
[Nia] androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }

 [My] androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
[Nia] room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }

 [My] truth = { module = "com.google.truth:truth", version.ref = "truthVersion" }
[Nia] truth = { group = "com.google.truth", name = "truth", version.ref = "truth" }

 [My] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
[Nia] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }

 [My] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
[Nia] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }

 [My] androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
[Nia] room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }

 [My] androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
[Nia] androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }

 [My] androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationRuntimeKtx" }
[Nia] androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }

 [My] androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
[Nia] room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }

 [My] androidx-ui-util = { group = "androidx.compose.ui", name = "ui-util" }
[Nia] androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" }

 [My] androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
[Nia] androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }

 [My] androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
[Nia] androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }

 [My] android-library = { id = "com.android.library", version.ref = "agp" }
[Nia] android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }

 [My] kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
[Nia] compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

 [My] android-application = { id = "com.android.application", version.ref = "agp" }
[Nia] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }

 [My] hilt = { id = "com.google.dagger.hilt.android", version.ref = "hiltVersion" }
[Nia] hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }

์ถœ๋ ฅ๋œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋‚ด ํ”„๋กœ์ ํŠธ์˜ ํ”„๋กœํผํ‹ฐ๋ช…๋“ค์„ ์ˆ˜์ •ํ–ˆ๋‹ค. ์ˆ˜์ • ํ›„ ๋‹ค์‹œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ดค๋Š”๋ฐ, ๋‹น์—ฐํ•˜๊ฒ ์ง€๋งŒ (๋‹ค๋ฅธ ์ ์ด ์—†์„ํ…Œ๋‹ˆ) ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š์•˜๋‹ค.