07
16

๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ๋ฅผ ๋ฒ ํƒ€๋กœ ๋ฐ€๊ณ ์žˆ๋‹ค๊ฐ€ ์—†์• ๋ฒ„๋ฆฌ๊ณ , ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ํ”„๋กœ์ ํŠธ์— ๋ฌด์กฐ๊ฑด ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ + gradle๋กœ ์ ์šฉํ•˜๋„๋ก ๋ฐ”๋€Œ์—ˆ๋‹ค... ๋”ฐ๋ผ์„œ toml์„ ์ด์ œ ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ์•„์•ผํ•œ๋‹ค.

 

๋˜ jellyfish์—์„œ ์ƒ์„ฑํ•œ View ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ฌ๋ผ์ง„ ์ฝ”๋“œ๋“ค์ด ๋งค์šฐ ๋งŽ์•„์„œ ์ด ๋ถ€๋ถ„๋“ค ๋จผ์ € ์žก๊ณ  ๊ฐ€์•ผ๊ฒ ๋‹ค. ์‹ฌ์ง€์–ด ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ๋„ ๋ณ€ํ™”๊ฐ€ ์ƒ๊ฒผ๋‹ค.

# edge-to-edge ๋””์Šคํ”Œ๋ ˆ์ด ๋Œ€์‘

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}

id๊ฐ€ main์ธ ๊ฒƒ์˜ ์ •์ฒด๋Š” MainActivity์˜ ์ œ์ผ ๋ฐ”๊นฅ์ชฝ ConstraintLayout์ด๋‹ค. ๋„ˆ๋ฌด ๋งŽ์ด ๋ฐ”๋€Œ์–ด์„œ ์ข€ ํ˜ผ๋ž€์Šค๋Ÿฌ์šด๋ฐ Activity๊ฐ€ ๋ฐ”๋€ ๊ฒƒ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด๊ฒ ๋‹ค.

 

์šฐ์„  enableEdgeToEdge๋Š” edge-to-edge ๋””์Šคํ”Œ๋ ˆ์ด ๋ชจ๋“œ(์ƒํƒœ๋ฐ”๊นŒ์ง€ ํ™•์žฅ๋˜๋Š”)๋ฅผ ํ™œ์„ฑํ™” ์‹œ์ผœ์ฃผ๋Š” ๋ถ€๋ถ„์ด๋‹ค. ์ƒํƒœ๋ฐ”์˜ ์ƒ‰์ƒ๋„ ์•Œ์•„์„œ ํˆฌ๋ช…ํ•˜๊ฒŒ ์กฐ์ ˆํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

 

์ง€๊ธˆ ์ฝ”๋“œ์—์„œ setOnApplyWindowInsetsListener๋Š” ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ์˜ ํŒจ๋”ฉ์„ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ด์ฃผ๋Š” ์š”์†Œ๋‹ค. ์ด๊ฒŒ ์™œ ํ•„์š”ํ•˜๋ƒ๋ฉด ์ง€๊ธˆ edge-to-edge๋ฅผ ํ™œ์„ฑํ™” ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ƒํƒœ๋ฐ”๋Š” ๋ฌผ๋ก ์ด๊ณ  ์‹œ์Šคํ…œ ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ” ์œ„์น˜๊นŒ์ง€ ํ™•์žฅ๋œ ๊ฑด๋ฐ ๊ทธ๋Ÿฌ๋ฉด ์•ฑ์˜ UI ์ค‘ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ถ€๋ถ„์ด ์ƒ๊ธด๋‹ค. ๊ทธ๊ฑธ ๋ง‰๊ธฐ์œ„ํ•ด inset ์„ค์ •์„ ํ•˜๋Š”๊ฒŒ ์ด ๋ฆฌ์Šค๋„ˆ๋‹ค.

 

insets๋ฅผ ๊ผญ ๋ฐ˜ํ™˜ํ•ด์ค˜์•ผ ์‹œ์Šคํ…œ์ด ์•Œ์•„๋จน๋Š”๋‹ค. insets์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‹ค๋ฅธ ๋ทฐ(๊ณ„์ธต๊ตฌ์กฐ์ƒ์œผ๋กœ ์กด์žฌํ•˜๋Š”)๋“ค๋„ ์ด id๋กœ ์ง€์ •ํ•ด๋‘” insets์„ ๊ฐ์ง€ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๊ณ  WindowInsetsCompat.CONSUMED๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‹ค๋ฅธ ๋ทฐ๋“คํ•œํ…Œ inset ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์•Œ๋ฆฌ์ง€์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค.

 

๊ผญ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋‹ฌ ํ•„์š”์—†์ด ์‹œ์Šคํ…œ ์š”์†Œ์— ๊ฐ€๋ ค์ง€์ง€ ์•Š๊ธธ ์›ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋‹ค. systemBars์— ์†ํ•˜๋Š” ์š”์†Œ๋Š” ์ƒํƒœํ‘œ์‹œ์ค„, ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ”, ์บก์…˜ ๋ฐ”๊ฐ€ ์žˆ๋‹ค. ํ‚ค๋ณด๋“œ์— ํ•ด๋‹นํ•˜๋Š” IME๋Š” ์—ฌ๊ธฐ์— ๋“ค์–ด๊ฐ€์ง€์•Š๋Š”๋‹ค.

https://developer.android.com/develop/ui/views/layout/edge-to-edge?hl=ko

 

์•ฑ์— ๋” ๋„“์€ ํ™”๋ฉด์— ์ฝ˜ํ…์ธ  ํ‘œ์‹œ  |  Views  |  Android Developers

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

developer.android.com

# toml(Tom's Obvious Minimal Language) + version catalog

gradle ๋‹ฌ๋ผ์ง„๊ฑฐ ๋จผ์ € ๋ณด์ž... ๋‹ค alias๋กœ toml์— ์„ ์–ธ๋œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์ด๋‹ค. ๋ฒ„์ „ ์นดํƒˆ๋กœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” buildscript ๋ธ”๋ก๊ณผ classpath๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹  plugins ๋ธ”๋ก์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ๋‹ค ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ํ•œ๋‹ค... 

// project ์ˆ˜์ค€ gradle
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
}
// app ์ˆ˜์ค€ gradle
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
}

android {
    namespace = "com.kimmandoo.hilt"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.kimmandoo.hilt"
        minSdk = 28
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    implementation(libs.androidx.activity)
    implementation(libs.androidx.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
}

buildSrc๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ณด๋‹ค ๋” ๊ฐ„๋‹จํ•ด์ง„ ๊ฒƒ ๊ฐ™๋‹ค. libs.versions.toml ํŒŒ์ผ ํ•˜๋‚˜์—์„œ ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•œ๋‹ค.

versions, libraries, plugins ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜์–ด์ ธ ์žˆ๊ณ , ๋ณด๊ณ  ์ดํ•ด๋ชปํ• ์ •๋„๋กœ ๋ณต์žกํ•˜๊ฒŒ ๊ธฐ์ˆ ๋˜์–ด์žˆ์ง„ ์•Š๋‹ค.

[versions]
agp = "8.4.1"
kotlin = "1.9.0"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
activity = "1.8.0"
constraintlayout = "2.1.4"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

์ด์ œ ์–ด๋–ป๊ฒŒ ์ƒ๊ธด์ง€๋Š” ํ™•์ธํ–ˆ๊ณ , gradle์— ์–ด๋–ป๊ฒŒ ์ถ”๊ฐ€ํ•  ์ง€๋ฅผ ์‹ค์Šตํ•ด๋ด์•ผํ•œ๋‹ค. hilt๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๋ฉด์„œ ์ ์šฉํ•ด๋ณด๊ฒ ๋‹ค.

 

# Hilt ์ถ”๊ฐ€ํ•˜๊ธฐ

Hilt๋Š” ์˜์กด์„ฑ ์ฃผ์ž…(DI) ๋„๊ตฌ๋กœ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์ด๊ณ  ๋ฏธ๋ฆฌ ์ •ํ•ด์ง„ ์ปดํฌ๋„ŒํŠธ(ํ‘œ์ค€ ์ปจํ…Œ์ด๋„ˆ)๊ฐ€ ์žˆ์–ด์„œ ๊ทธ๊ฑธ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ง€์ •ํ•ด์„œ ์“ฐ๋Š” ํ˜•ํƒœ์ด๋‹ค.

https://dagger.dev/hilt/gradle-setup

 

Gradle Build Setup

Hilt dependencies To use Hilt, add the following build dependencies to the Android Gradle module’s build.gradle file: dependencies { implementation 'com.google.dagger:hilt-android:2.51.1' annotationProcessor 'com.google.dagger:hilt-compiler:2.51.1' // Fo

dagger.dev

๋‚˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ๋กœ ksp๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

 

hilt๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ๋ฃจํŠธ์ˆ˜์ค€, ๋ชจ๋“ˆ ์ˆ˜์ค€ gradle ํŒŒ์ผ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ dependency์— ์ถ”๊ฐ€ํ•ด์ค˜์•ผํ•œ๋‹ค. toml ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด์„œ ์ด์ „์— ์‚ฌ์šฉํ•˜๋˜ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๋ผ์„œ, ๋ณต์žกํ•œ๊ฒŒ ์‹ซ๋‹ค๋ฉด ๊ทธ๋ƒฅ ๋ฌธ์„œ๋Œ€๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

[versions]
hilt = "2.51.1"
ksp = "1.9.0-1.0.13"

[libraries]
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" }

[plugins]
hilt-gradle-plugin = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

๋ฌธ์„œ์— ์žˆ๋Š” ๊ฑธ toml๋ฐฉ์‹์œผ๋กœ ๋ฐ”๊ฟ”์„œ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์‚ฌ์šฉ๋œ dash(-)๋Š” ๋‚˜์ค‘์— (.)์œผ๋กœ ์ฐธ์กฐํ•˜๊ฒŒ๋œ๋‹ค. ๋‚˜๋Š” ์ฝ”ํ‹€๋ฆฐ ๋ฒ„์ „์ด 1.9.0์ด๋ผ์„œ ksp๋ฒ„์ „์„ 1.9.0-1.0.13์œผ๋กœ ๋งž์ถฐ์คฌ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•ด์คฌ๋‹ค๋ฉด ํ”„๋กœ์ ํŠธ ์ˆ˜์ค€ gradle์— ์•„๋ž˜์ฒ˜๋Ÿผ ์ถ”๊ฐ€ํ•œ๋‹ค. apply false๋Š” ํ”„๋กœ์ ํŠธ ๋‚ด ๋ชจ๋“ˆ์— ์ „์ฒด์ ์šฉํ• ์ง€ ๋ฌผ์–ด๋ณด๋Š”๊ฑด๋ฐ ๋ณดํ†ต false๋กœ ๋‘”๋‹ค.

plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.hilt.gradle.plugin) apply false
    alias(libs.plugins.ksp) apply false
}

๋ชจ๋“ˆ ์ˆ˜์ค€ gradle์˜ plugin์—๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ถ”๊ฐ€ํ•œ๋‹ค.

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.ksp)
    alias(libs.plugins.hilt.gradle.plugin)
}

๊ทธ๋ฆฌ๊ณ  dependeny๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋์ด๋‹ค.

dependencies {
	...
    // hilt
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)
    androidTestImplementation(libs.hilt.android.testing)
    kspAndroidTest(libs.hilt.compiler)
}

ํ•œ๊ฐ€์ง€ ์ฃผ์˜ํ•  ์ ์€ tomlํŒŒ์ผ์— ์„ ์–ธํ•˜๊ณ  ๋‚˜์„œ gradle sync๋ฅผ ํ•ด์ค˜์•ผ gradle ํŒŒ์ผ์— plugin ์ถ”๊ฐ€ํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์˜ฌ๋ผ๊ฐ„๋‹ค.

์ด๋Ÿฌ๋ฉด ํ•„์ˆ˜์‚ฌํ•ญ์€ ๋‹ค ๋งˆ์ณค๋‹ค(test๋Š” ์„ ํƒ์‚ฌํ•ญ). 

kapt๋ฅผ ๊ธฐ์กด์— ์ด์šฉํ•˜๋˜ ์‚ฌ๋žŒ๋“ค์€ corretErrorTypes๊นŒ์ง€ ์ฒ˜๋ฆฌํ•ด์คฌ์„ํ…๋ฐ ksp๋Š” ์ž๋ฐ” ๋ณ€ํ™˜ ๊ณผ์ •์ด ์—†์–ด์„œ ์ด๊ฑธ ์•ˆ๋‹ฌ์•„ ์ค˜๋„ ๋œ๋‹ค.

 

toml... ๋ญ”๊ฐ€ ํŽธํ•œ๋“ฏ ์•ˆํŽธํ•˜๊ธดํ•œ๋ฐ renovatebot๊ฐ™์€ ๋„๊ตฌ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „๊ด€๋ฆฌ๋ฅผ ํ•œ๋‹ค๋ฉด ๊ผญ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT