๊พธ์ค€ํ•จ์ด ์ง„๋ฆฌ๋‹ค!!

์–ด์ œ๋ณด๋‹ค ๋ฐœ์ „ํ•œ ์˜ค๋Š˜์ด ๋˜๊ณ ํ”ˆ ๐Ÿง‘๐Ÿปโ€๐Ÿ’ป ์˜ ๋ธ”๋กœ๊ทธ

Android/Kotlin

[Kotlin][Android] ์•ˆ๋“œ๋กœ์ด๋“œ Hilt๋ฅผ ํ†ตํ•œ ์˜์กด์„ฑ ์ฃผ์ž…๋ฐฉ๋ฒ•

๋ށ์š” 2023. 1. 14. 12:44

์•ˆ๋…•ํ•˜์„ธ์š” ๐Ÿ‘‹

์˜ค๋Š˜์€ ์•ˆ๋“œ๋กœ์ด๋“œ Hilt์˜ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Hilt๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋จผ์ €

์˜์กด์„ฑ ์ฃผ์ž…์ด๋ž€ ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)์ด๋ž€?

์˜์กด์„ฑ ์ฃผ์ž…์ด๋ž€ class์™€class ๊ฐ„ ์˜์กด์„ฑ์— ๋Œ€ํ•œ ์ฃผ์ž…์„ ์™ธ๋ถ€์—์„œ ํ•จ์œผ๋กœ์จ class ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”์–ด 

์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•์„ ์˜๋ฏธ ํ•ฉ๋‹ˆ๋‹ค.

/** ์˜์กด์„ฑ์ด ์ฃผ์ž…๋˜์ง€ ์•Š์€ ์ฝ”๋“œ **/
class Foo(){
	val noDI = NoDepedency()
    
    fun somethingTodo() {
    	noDI.foo()
    }
}

class NoDependency(){
	fun foo()
}

์˜์กด์„ฑ์ด ์ฃผ์ž…๋˜์ง€ ์•Š์€ ์ฝ”๋“œ๋Š” ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด NoDepedency๋ผ๋Š” class๋ฅผ Foo์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Foo class ๋‚ด๋ถ€์—์„œ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜์–ด

์ฝ”๋“œ์˜ ๊ฒฐํ•ฉ๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/** ์˜์กด์„ฑ์ด ์ฃผ์ž…๋œ ์ฝ”๋“œ **/
class Foo(private val di : Dependency) {
	
    fun somethingTodo() {
    	di.foo()
    }
}

class Dependency {
	fun foo()
}

๋ฐ˜๋ฉด์— ์˜์กด์„ฑ์„ ์ฃผ์ž… ์‹œ์—๋Š” ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด Foo์—์„œ ์‚ฌ์šฉํ•  Dependency๋ผ๋Š” class๋ฅผ ์™ธ๋ถ€์—์„œ ์ „๋‹ฌ๋ฐ›์Œ์œผ๋กœ Dependency๋ผ๋Š” class์™€ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ( Dependency๋ฅผ ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜์–ด๋„ Foo์—์„œ๋Š” ํ•  ์ผ์ด ์—†๋‹ค. )


DI์˜ ์žฅ์ 

  •  ๊ฐ๊ฐ์ด ๋…๋ฆฝ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ดํ•˜๋‹ค.
  •  Boilerplate code๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด Hilt๋Š” ๋ฌด์—‡์ผ๊นŒ์š”?

Hilt๋ž€?

class๋ฅผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ ์ƒ๋ช…์ฃผ๊ธฐ(์ƒ์„ฑ~์†Œ๋ฉธ๋˜๊ธฐ๊นŒ์ง€)์˜ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด ์ฃผ๊ธฐํ•ด์ฃผ๊ธฐ ์œ„ํ•ด Google์—์„œ๋Š” Dagger ๊ธฐ๋ฐ˜์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ ๊ทธ๊ฒƒ์ด ๋ฐ”๋กœ 

Dagger Hilt์ž…๋‹ˆ๋‹ค.

 ์ฆ‰ , ๊ฐ instance์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•ด ์ฃผ์–ด ์˜์กด์„ฑ ์ฃผ์ž…์„ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ๋งŒ๋“  google์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๐Ÿค”

 

Hilt๋Š” Annotaion์„ ํ†ตํ•ด ์•ฑ์„ ์„ค์ •ํ•˜๊ณ  ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ Annotaion๋“ค๊ณผ ์„ค๋ช… ๋ฐ ์‚ฌ์šฉ๋ฒ•์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


์‹œ์ž‘ํ•˜๊ธฐ์— ์•ž์„œ Hilt๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์ ํŠธ ์„ค์ • ๋ถ€ํ„ฐ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

build.gradle ์„ค์ •

// Project
plugins {
	...
    id 'com.google.dagger.hilt.android' version '2.44' apply false // Hilt
}
// Module

plugins {
	...
    // Hilt
    id 'kotlin-kapt'
    id 'com.google.dagger.hilt.android'
}

dependencies {
	...
    // Hilt
    implementation "com.google.dagger:hilt-android:2.44"
    kapt "com.google.dagger:hilt-compiler:2.44"
    implementation "androidx.activity:activity-ktx:1.6.1"
    implementation "androidx.fragment:fragment-ktx:1.5.5"
}

@HiltAndroidApp

Hilt๋Š” Application ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๋”ฐ๋ฅด๋ฉฐ ์ปดํŒŒ์ผ ๋‹จ๊ณ„ ์‹œ DI์— ํ•„์š”ํ•œ ๊ตฌ์„ฑ์š”์†Œ๋“ค์„ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, Hilt์—๊ฒŒ Application ์˜์กด์„ฑ ์ฃผ์ž…์˜ ์‹œ์ž‘์ ์„ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

@HiltAndroidApp
class MainApplication : Application() {
}

@AndroidEntryPoint

@HiltAndroidApp๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Android ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์˜์กด์„ฑ ์ฃผ์ž… ์‹œ์ž‘์ ์„ ์•Œ๋ ค์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
  ...
}

@Inject

ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class MyRepositoryImpl @Inject constructor(
    private val myApi: MyApi
) : MyRepository {

}

์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด @Inject constructor๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ƒ์„ฑ์ž์—์„œ ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ณ€์ˆ˜์— ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ ๋ฐ”๋กœ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

ํ•˜์ง€๋งŒ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜น์€ interface๋“ค์˜ ๊ฒฝ์šฐ Hilt์—๊ฒŒ ์–ด๋–ป๊ฒŒ ์ธ์Šคํ„ด์Šคํ™”๋ฅผ ํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•ด ์ค„ ๊ฒƒ์ธ์ง€ ์•Œ๋ ค์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ ๊ทธ ์—ญํ• ์ด ๋ฐ”๋กœ @Module์ž…๋‹ˆ๋‹ค.

 

@Module

Hilt์—๊ฒŒ ์–ด๋–ป๊ฒŒ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋ฉด ๋˜๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ๋กœ ์˜์กด์„ฑ ์ œ๊ณต ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” @Provides์™€ @Binds๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@InstallIn

Hilt์—๊ฒŒ ํ•ด๋‹น ๋ชจ๋“ˆ์ด ์–ด๋А Scope์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

}

Hilt์˜ Components 

๊ฐ๊ฐ์˜ Component๋“ค์€ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ ์•„๋ž˜ ํ‘œ๋ฅผ ํ†ตํ•ด ์–ธ์ œ ์ƒ์„ฑ~์†Œ๋ฉธ๋˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Component๋“ค์˜ ์ƒ๋ช…์ฃผ๊ธฐ

@ Binds

Hilt์—๊ฒŒ ๊ฐ์ฒด๋ฅผ ์–ด๋–ป๊ฒŒ ์ธ์Šคํ„ด์Šคํ™”ํ•˜์—ฌ ์ œ๊ณตํ•ด ์ค„ ๊ฒƒ์ธ์ง€ ์•Œ๋ ค์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ํ”„๋กœ์ ํŠธ์—์„œ ์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•ด ์ œ๊ณตํ•ด ์ค„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. (๋งŒ๋“  interface๋‚˜ ์ถ”์ƒ class ๋“ฑ)

 

1. ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ

interface MyRepository {
    suspend fun doNetworkCall() : Response<T>
}

2. ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

class MyRepositoryImpl @Inject constructor(
    private val myApi: MyApi
) : MyRepository {
    override suspend fun doNetworkCall(): Response<T> {
        return myApi.doNetworkCall()
    }
}

3. Binds๋ฅผ ํ†ตํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์ œ๊ณต๋ฐฉ๋ฒ•์„ Hilt์—๊ฒŒ ์•Œ๋ ค์คŒ

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    @Singleton
    abstract fun bindMyRepository(myRepositoryImpl: MyRepositoryImpl) : MyRepository
}

4. ์˜์กด์„ฑ ์ฃผ์ž…

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository
) : ViewModel(){

}

 

@Provides

@Binds์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Hilt์—๊ฒŒ ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด์ง€๋งŒ, ์ฃผ๋กœ ์™ธ๋ถ€๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ์ฒด๋ฅผ ํ•„์š”๋กœ ํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ( Room , Retrofit , OkHttp ๋“ฑ )

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideMyApi() : MyApi {
        return Retrofit.Builder()
            .baseUrl(BEACH_CONGESTION_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(MyApi::class.java)
    }
}

 

 

ํ•ด๋‹น ํฌ์ŠคํŒ…์—์„œ๋Š” ๋‹ค๋ฃจ์ง€ ์•Š๊ฒ ์ง€๋งŒ ์ด ์™ธ์—๋„ ๋งŽ์€ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿคฉ

Hilt๋Š” ๋งค์šฐ ํŽธ๋ฆฌํ•˜๊ณ  ๋‹ค๋ฅธ DI๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋น„ํ•ด ๋ฐฐ์šฐ๊ธฐ ์‰ฌ์šด ์žฅ์ ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์กฐ๊ธˆ ๋” ๊ณต๋ถ€ํ•˜์—ฌ ์ต์ˆ™ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค๋ฉด ์•„์ฃผ ์ข‹์€ ๋ฌด๊ธฐ๊ฐ€ ๋  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

 

์ด์ƒ์œผ๋กœ Hilt ํฌ์ŠคํŒ…์„ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆ์ฝ” ํ•˜์„ธ์š” :)