#1 ๊ฐ์
#1-1 Hilt ๋์
๊ณํํ ์, Hilt ๋์ ์ ๋์๋ฝ ๋จ๊ณ์๋ค. ํ์ง๋ง, ์ฝ๋๋ฅผ ์ง๊ฐ๋ฉฐ ์ฑ์ ๊ตฌํํด๋๊ฐ๋ ๋ฐ์ ์ฌ๋ฌ ์์ฉ๊ตฌ ์ฝ๋๋ค์ด ๋๋ฅผ ๊ฑฐ์ฌ๋ฆฌ๊ฒ ํ๋ค. Hilt๋ก ํ์กดํ๋ ์์ฉ๊ตฌ ์ฝ๋ ๊ทธ๋ฆฌ๊ณ ์ ์ฌ์ ์ผ๋ก ๋ฐ์ํ ์์ฉ๊ตฌ ์ฝ๋๋ค์ ์ ์ ์ ์ผ๋ก ์ ๊ฑฐํ๋ ํธ์ด ๋ ์ข์ ๊ฒ์ด๋ ํ๋จ์ ๋ด๋ ธ๋ค.
#1-2 ๊ธฐ๋ฐ (๋ ํผ๋ฐ์ค)
[Android] Dagger2 - Hilt๋ก ๋ง์ด๊ทธ๋ ์ด์
#1 Hilt ๊ฐ์ Hilt๋ฅผ ์ฌ์ฉํ ์ข ์ ํญ๋ชฉ ์ฝ์ | Android Developers์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. Hilt๋ฅผ ์ฌ์ฉํ ์ข ์ ํญ๋ชฉ ์ฝ์ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ
kenel.tistory.com
์ ๊ฒ์๊ธ์ ๊ธฐ๋ฐํด, ๋ณธ ์๋๋ก์ด๋์ Hilt๋ฅผ ๋์ ํ๋ค.
#2 ์ฝ๋ - ํ๊ฒฝ ์ค์
#2-1 ๋ชจ๋ ์์ค build.gradle.kts
plugins {
...
// KSP ๋ฐ kapt (์ด๋
ธํ
์ด์
์ฝ๊ธฐ์ฉ)
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.kapt")
// Hilt
id("com.google.dagger.hilt.android")
}
android {
...
}
dependencies {
...
// Hilt
implementation(libs.hilt.android)
kapt(libs.hilt.android.compiler)
ksp(libs.hilt.compiler)
implementation(libs.androidx.hilt.navigation.compose) // Hilt์ Jetpack Compose์ ViewModel์ ํจ๊ป ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (hiltViewModel() ์ฌ์ฉ ๊ฐ๋ฅ)
}
#1-2์์ ๋งํฌํ ๋ ํผ๋ฐ์ค ๊ฒ์๊ธ์ build.gradle๋ง์ผ๋ก๋ ํ์ ํ hiltViewModel()์ ์ฌ์ฉํ ์ ์๋ค. hiltViewModel()์ ์ฌ์ฉํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ "androidx.hilt:hilt-navigation-compose"๋ฅผ dependencies์ ๋ฃ์๋ค.
#2-2 Application ํด๋์ค
package com.example.nutri_capture_new.di
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class HiltApplication : Application()
#1-2์ ์ํ๋ฉด, Hilt๋ @HiltAndroidApp ์ด๋ ธํ ์ด์ ์ด ๋ถ์ Application ํด๋์ค๋ฅผ ์๊ตฌํ๋ค. ๋, ์ด Application ํด๋์ค ๋ฐ ์์กด์ฑ ์ฃผ์ ์ ๊ด๋ จ๋ ๋ชจ๋ ํด๋์ค๋ฅผ ๋ฃ์ ํจํค์ง์ธ "di" ํจํค์ง๋ฅผ ์ ์คํ๋ค.
#2-3 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".di.HiltApplication"
...>
<activity
...
</activity>
</application>
</manifest>
๋ฐฉ๊ธ ๋ง๋ Application ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ค.
#3 ์ฝ๋ - Activity
#3-1 Activity
...
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
// nutrientViewModel์ด Hilt์ viewModels()์ ์ํด ๊ด๋ฆฌ๋๋๋ก ์์(by). ๋ฐ๋ผ์ ๋ฐ๋์ by ํค์๋๋ก ์ ์ธํด์ผํจ.
private val nutrientViewModel: NutrientViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
setContent {
NutricapturenewTheme {
Scaffold(
...
bottomBar = {
NutrientChatBar()
}
) { ... ->
Box(
...
) {
NutrientScreen()
}
}
}
}
}
}
@AndroidEntryPoint๊ฐ ์ ์ธ๋ Activity์์ ViewModel์ ๋ง๋ค๋ฉด, ์ด Activity ๋ด์ ๋ชจ๋ ํ์ Composable์์ ๋์ผํ ViewModel (์ฑ๊ธํค) ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ๋ค. ํ์ ์ปดํฌ์ ๋ธ ํจ์์ธ NutrientChatBar()๋ NutrientScreen()์์ nutrientViewModel๋ฅผ ๊ณต์ ํด์ ์ฌ์ฉํ๋ค๋ ์๊ธฐ๋ค.
#3-2 Activity์ ํ์ ์ปดํฌ์ ๋ธ
@Composable
fun NutrientScreen(
viewModel: NutrientViewModel = hiltViewModel()
) {
...
}
@Composable
fun NutrientChatBar(
viewModel: NutrientViewModel = hiltViewModel()
) {
...
}
hiltViewModel()์ ์ฌ์ฉํ๋ฉด ํ์ฌ Activity๋ Fragment์์ Hilt๊ฐ ๊ด๋ฆฌํ๋ ViewModel ์ธ์คํด์ค๋ฅผ ์๋์ผ๋ก ๊ฐ์ ธ์จ๋ค (Hilt๊ฐ ๊ด๋ฆฌํ๋ ViewModel์ด๋ @HiltViewModel ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ViewModel(#4-3)์ ์๋ฏธํ๋ค). hiltViewModel()์ ViewModelStoreOwner(์: Activity, Fragment)์ ๋ฒ์ ๋ด์์ ViewModel์ ์๋์ผ๋ก ์ฐพ์ ์ฃผ์ ํ๋ ๋ฐฉ์์ผ๋ก ์๋ํ๋ค. ์ด ์ฝ๋์์๋ #3-1์ nutrientViewModel๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋๋ค.
๋ง์ฝ viewModelStoreOwner์ ํด๋น ViewModel์ด ์๋ค๋ฉด, ๋ค์ ๋งํด ์ฌ๊ธฐ์ MainActivity ๋ด์ nutrientViewModel์ด ์๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ์ด ๊ฒฝ์ฐ hiltViewModel()์ ์๋ก์ด ViewModel ์ธ์คํด์ค๋ฅผ (์ด๋์์ ๊ฐ์ ธ์ค๋๊ฒ ์๋๋ผ ๊ทธ ์๋ฆฌ์์) ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ค. ์ด ๊ฒฝ์ฐ์, ViewModel์ ์๋ช ์ฃผ๊ธฐ(๋ฒ์)๋ ํด๋น ์ปดํฌ์ ๋ธ์ ์๋ช ์ฃผ๊ธฐ์ ์ข ์๋๋ค.
ํ๋ก์ ํธ์ ์๋ ViewModelFactory๋ ์ด์ ์ญ์ ํ๋ค. Hilt ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์์์(์์์ ์ผ๋ก) ViewModel์ ์ธ์๋ฅผ ์ฃผ์ ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
#4 ์ฝ๋ - Room
#4-1 Database, DAO
package com.example.nutri_capture_new.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
@Database(
entities = [
Day::class,
Meal::class
],
views = [DayMealView::class],
version = 1
)
@TypeConverters(Converters::class)
abstract class MainDatabase : RoomDatabase() {
abstract val mainDAO: MainDAO
/* ์ญ์
companion object {
@Volatile
private var INSTANCE: MainDatabase? = null
fun getInstance(context: Context): MainDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
MainDatabase::class.java,
"main_database"
).build()
INSTANCE = instance
}
return instance
}
}
}
/*
}
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ธฐ์กด ๋ฐฉ์์ MainDabase์ companion object๋ฅผ ์ด์ฉํ๋ ๊ฑฐ์๋ค. ์ด์ ๋ Hilt๋ฅผ ํตํด ์์์ ์ผ๋ก ์ด ์์ ์ ์ํํ ๊ฒ์ด๋ฏ๋ก ํด๋น ์ฝ๋๋ฅผ ์ ๊ฑฐํ๋ค.
package com.example.nutri_capture_new.di
import android.content.Context
import androidx.room.Room
import com.example.nutri_capture_new.db.MainDAO
import com.example.nutri_capture_new.db.MainDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): MainDatabase {
return Room.databaseBuilder(
context,
MainDatabase::class.java,
"main_database"
).build()
}
@Provides
fun provideMainDAO(database: MainDatabase): MainDAO {
return database.mainDAO
}
}
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ DAO๋ @Inject ๋์ @Provides ๋ฐฉ์์ ์ฌ์ฉํด์ผ ํ๋ค. ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด, "ํด๋์ค๊ฐ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณต๋๋ฏ๋ก ํด๋์ค๋ฅผ ์์ ํ์ง ์์ ๊ฒฝ์ฐ(Retrofit, OkHttpClient ๋๋ Room ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฐ์ ํด๋์ค) ๋๋ ๋น๋ ํจํด์ผ๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํด์ผ ํ๋ ๊ฒฝ์ฐ"์๋ ์์ฑ์ ์ฝ์ (@Inject ์ด๋ ธํ ์ด์ )์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ด๋ค (์ฐธ์กฐ: @Provides์ ๋ชฉ์ ).
์ด ์ฝ๋ ์ดํ๋ก, ์ด์ Provides ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ DAO๊ฐ ์ฃผ์ ๋๋ค. provideDatabase()๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค๋ฅผ provideMainDAO()์ ์ ๋ฌํ๊ณ , provideMainDAO()๋ DAO ์ธ์คํด์ค๋ฅผ ๋ฑ๋๋ค.
#4-2 Repository
package com.example.nutri_capture_new.db
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import javax.inject.Inject
class MainRepository @Inject constructor(private val dao: MainDAO) {
...
}
provideMainDAO()์ ์ํด ์ธ์ dao๊ฐ ์ฃผ์ ๋๋ค.
#4-3 ViewModel
...
@HiltViewModel
class NutrientViewModel @Inject constructor(private val repository: MainRepository) : ViewModel() {
...
}
์ธ์ repository์ ํด๋์ค๋ช MainRepository๋ฅผ Hilt๊ฐ ๊ฐ์ง + ๊ฒ์ํ์ฌ ๊ทธ ์ธ์คํด์ค๋ฅผ ์์์ ์ฃผ์ ํด์ค๋ค.
#5 ์์ฑ๋ ์ฑ
#5-1 ์์กด์ฑ ๊ทธ๋ํ

๋ณธ ๊ฒ์๊ธ์์ ๋ค๋ฃฌ ํด๋์ค๋ค์ ์์กด์ฑ์ ๋์ํ๋ก ํํํ๋ฉด ์์ ๊ฐ๋ค. ํ์ดํ๋ ํด๋์ค ๊ฐ์ ์ข ์์ฑ์ ๋ํ๋ธ๋ค. ์๋ฅผ ๋ค์ด, MainActivity๋ NutrientScreen์ ์ข ์๋๋ค. ์ข ์์ ์ฌ์ ์ ์๋ฏธ๋ '์์ฃผ์ฑ์ด ์์ด ์ฃผ๊ฐ ๋๋ ๊ฒ์ ๋ธ๋ ค ๋ถ์'์ด๋ค. ์ข ์์ '์์์ผ ํ๋ค'๋ผ๋ ๋ง๋ก๋ ํํํ ์ ์๋ค. ๋ฐ๋ผ์ MainActivity๋ NutrientScreen์ ๋ํด ์์์ผ ํ๋ค. ๋ฐ๋ฉด, NutrientScreen์ MainActivity๋ฅผ ๋ชฐ๋ผ๋ ๋๋ค. NutrientScreen์ ์ค๊ณํ ๋ MainActivity์์ ๋ญ ์ด๋ป๊ฒ ํ ์ง ์ ํ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ค๋ ๊ฒ์ด๋ค (๋์ , NutrientScreen์ NutrientViewModel์ ๋ํด ์ข ์์ ์ด๋ฏ๋ก NutrientViewModel์ ์ฐธ์กฐํ๋ฉฐ ์ค๊ณํด์ผ ํ๋ค). '์์์ผ ํ๋ ์ชฝ'์์ '๋ชฐ๋ผ๋ ๋๋ ์ชฝ'์ผ๋ก ํ์ดํ๋ฅผ ์ด์ ๊ฒ์ด ์ ๋์๋๋ค.
#5-2 ์ด ๊ฒ์๊ธ ์์ ์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
#5-3 ๋ณธ ํ๋ก์ ํธ์ ๊ฐ์ฅ ์ต์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
'๊ฐ๋ฐ ์ผ์ง ๐ป > Nutri Capture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Nutri Capture ํ๋ก ํธ์๋ - 'ํผ์' ์์ด์ฝ ๊ตฌํ (0) | 2025.03.20 |
---|---|
Nutri Capture ํ๋ก ํธ์๋ - ์ปค์คํ BottomSheetScaffold ๊ฐ๋ฐ ์ ์ (0) | 2025.03.19 |
Nutri Capture ํ๋ก ํธ์๋ - windowInsetsPadding() (0) | 2025.01.29 |
Nutri Capture - ์ฝ๋ ์ ๋ฆฌ (0) | 2025.01.29 |
Nutri Capture ํ๋ก ํธ์๋ - ์์ด์ฝ ์ ์ (1์ฐจ) (0) | 2025.01.12 |