주완 님의 블로그
DI - koin & Hilt 본문
Dependency Injection (DI)의 개요 및 필요성
- *DI(Dependency Injection, 의존성 주입)**는 객체 간의 결합도를 낮추고, 유연하고 확장 가능한 애플리케이션 구조를 구현하기 위한 설계 패턴입니다.
클래스 간 강한 결합(Strong Coupling)은 코드의 재사용성과 유지보수성을 떨어뜨리며, 하나의 클래스가 변경될 경우 연쇄적으로 관련된 모든 클래스에 영향을 미칠 수 있습니다. 이를 해결하기 위해 의존 객체를 외부에서 주입(Inject)받는 방식으로 설계를 분리하며, 이러한 방식을 가능하게 해주는 외부 컨테이너를 DI 컨테이너라고 합니다.
Android의 종속 항목 삽입 | App architecture | Android Developers
Android의 종속 항목 삽입 | App architecture | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Android의 종속 항목 삽입 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 종속 항목 삽입(DI)은 프로그래
developer.android.com
DI 사용의 주요 목적
- 클래스 재사용 가능 및 종속 항목 분리 , 리팩터링 편의성, 테스트 편의성 으로 말합니다
- 클래스 재사용 및 종속 항목 분리
- 구현체 교체가 용이하며, 클래스가 직접 의존 객체를 생성하지 않기 때문에 다양한 환경에서 재사용 가능합니다.
- 리팩터링의 용이성
- 의존성 주입을 통해 객체 구성 시점에 종속성을 확인할 수 있어 구현 세부사항이 드러나지 않으며, 안정적인 구조를 유지할 수 있습니다.
- 테스트 편의성 향상
- 테스트 환경에서 목(Mock) 객체 등 다양한 구현체를 주입하여 독립적인 테스트가 가능합니다.
Koin: Kotlin 기반의 경량 DI 프레임워크
Koin은 Kotlin에 최적화된 DSL 기반 DI 프레임워크로, Android 개발 환경에서 간편하게 사용할 수 있도록 설계되었습니다.
Koin의 특징
- Kotlin DSL 사용
- 런타임 의존성 주입 방식
- Android Jetpack(ViewModel 등)과의 높은 호환성
- 러닝커브가 낮아 빠르게 적용 가능
- 어노테이션이 불필요하여 컴파일 시간이 짧음
단점
- 런타임 시점에 의존성 해석 → 성능 저하 가능
- 리플렉션 사용으로 인한 성능 이슈
- koin.get() 등 직접 호출 시 모듈 간 의존성 관리가 어려워 멀티모듈 구조에 불리
💡
*리플렉션(Reflection)**은 프로그램이 실행 중(run-time)에 객체의 클래스, 메서드, 필드 등의 정보를 조회하고 조작할 수 있게 해주는 메커니즘입니다.
Koin 사용 예시
Gradle 설정
kotlin
복사편집
// build.gradle
implementation "io.insert-koin:koin-core:3.2.0"
implementation "io.insert-koin:koin-android:3.2.0"
implementation "io.insert-koin:koin-android-compat:3.2.0"
implementation "io.insert-koin:koin-androidx-workmanager:3.2.0"
implementation "io.insert-koin:koin-androidx-navigation:3.2.0"
Application 클래스 설정
kotlin
복사편집
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApplication)
androidLogger()
modules(appModule)
}
}
}
AndroidManifest.xml 설정:
xml
복사편집
<applicationandroid:name=".MyApplication"
... >
Module 정의
kotlin
복사편집
val appModule = module {
single { Animal() }
factory { Cat(get()) }
viewModel { MainViewModel() }
}
의존성 주입
kotlin
복사편집
class MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModel()
private val cat2: Cat by inject()
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val cat = get<Cat>()
cat.speek()
cat2.speek()
viewModel.doSomething()
}
}
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
}
private val viewModel: MainViewModel by viewModel()
private val cat2: Cat by inject()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.w("MainFragment", "cat =================================================")
val cat = get<Cat>()
cat.speek()
Log.w("MainFragment", "cat2 =================================================")
cat2.speek()
Log.w("MainFragment", "viewModel =================================================")
viewModel.doSomething()
}
}
Hilt: Android 공식 DI 프레임워크
Hilt는 Google이 제공하는 Android 전용 DI 프레임워크로, Dagger 기반에서 구축되어 Android 컴포넌트 통합 및 수명주기 관리 기능을 제공합니다.
Hilt의 장점
- 컴파일 타임에 의존성 그래프 생성 → 빠른 실행 성능
- Android 컴포넌트(Activity, Fragment 등)에서 손쉬운 주입
- Dagger 기반의 안정성과 유연성을 간편한 API로 제공
- Jetpack 및 Android 아키텍처 컴포넌트와 강력하게 통합됨
단점
- 학습 곡선이 있으며, 어노테이션 기반으로 구조에 대한 명확한 이해가 필요
1. Gradle 설정
groovy
복사편집
// project-level build.gradle
buildscript {
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:2.48"
}
}
// app-level build.gradle
plugins {
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"
}
2. Application 클래스에 Hilt 적용
kotlin
복사편집
@HiltAndroidApp
class MyApplication : Application()
@HiltAndroidApp 어노테이션을 통해 Hilt DI 컨테이너를 애플리케이션 전체에 적용합니다.
3. 의존성 제공 모듈 생성
kotlin
복사편집
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideAnimal(): Animal {
return Animal()
}
}
@Module + @InstallIn으로 Hilt에 의존성 제공 모듈을 등록하며, @Provides로 구체 인스턴스를 반환합니다.
4. 의존성 사용 클래스 정의
kotlin
복사편집
class Cat @Inject constructor(private val animal: Animal) {
fun speek() {
Log.d("Cat", "speek")
animal.speek("Cat")
}
}
@Inject 생성자를 통해 의존 객체가 자동으로 주입됩니다.
5. ViewModel에 Hilt 적용
kotlin
복사편집
@HiltViewModel
class MainViewModel @Inject constructor() : ViewModel() {
fun doSomething() {
Log.d("MainViewModel", "작업 수행 중")
}
}
ViewModel 주입을 위해 @HiltViewModel과 생성자 @Inject가 필요합니다.
6. Fragment에서 주입받기
kotlin
복사편집
@AndroidEntryPoint
class MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModels()
@Inject lateinit var cat: Cat
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cat.speek()
viewModel.doSomething()
}
}
@AndroidEntryPoint를 선언한 후 @Inject 혹은 by viewModels()로 의존 객체를 주입받습니다.
Koin vs Hilt
대표적인 DI 라이브러리인 Koin과 Hilt 중에서 어느 라이브러리를 사용해야할까요? 각 라이브러리의 장단점이 다르기에 어떤 라이브러리를 추천하기는 어렵습니다. 진행하려는 프로젝트의 규모와 서비스 방향에 따른 앱 구성에 따라서 적절하게 라이브러리를 선택하면 됩니다
사용성 예시 차이
Koin (Kotlin DSL 사용):
kotlin
복사편집
val appModule = module {
single { Animal() }
factory { Cat(get()) }
}
Hilt (Annotation 기반 사용):
kotlin
복사편집
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideAnimal(): Animal = Animal()
}
→ Koin은 코틀린 코드로 직관적이고 선언적으로 정의할 수 있는 반면,
→ Hilt는 어노테이션과 코드 생성기를 통해 정확하지만 복잡한 구성을 요구합니다.

Koin vs Hilt 비교
항목 Koin Hilt
| 기반 기술 | Kotlin DSL | Dagger 기반 (Annotation Processor) |
| 의존성 주입 시점 | 런타임 (동적 주입) | 컴파일 타임 (정적 분석) |
| 성능 | 상대적으로 낮음 (리플렉션 사용) | 높음 (리플렉션 사용 없음) |
| 러닝 커브 | 낮음 (빠르게 도입 가능) | 높음 (개념/구조 이해 필요) |
| 안드로이드 컴포넌트 통합 | 간접 지원 (ViewModel 등 일부 수동 처리 필요) | Android 컴포넌트에 최적화 (Activity, Fragment 등 자동 주입 지원) |
| 모듈화 지원 (멀티모듈) | 상대적으로 불리 (의존성 공유 어려움) | 강력한 모듈 분리 지원 (공식적으로 멀티모듈 설계 고려됨) |
| ViewModel 주입 | 별도 라이브러리 필요 (koin-androidx-viewmodel) | 기본 지원 (@HiltViewModel) |
| 테스트 환경 구성 | 쉬움 (Mock 주입 간단) | 상대적으로 복잡 (테스트 전용 모듈 구성 필요) |
| 빌드 속도 | 빠름 (어노테이션 미사용) | 상대적으로 느릴 수 있음 (KAPT 사용) |
| 학습자료 / 문서화 | 비교적 적음 (비공식 자료 다수) | Android 공식 문서 및 예제가 풍부 |
내부 동작 구조 차이
측면 Koin Hilt
| DI 그래프 생성 시점 | 앱 실행 중 (런타임) | 컴파일 시점 |
| 문제 발생 시점 | 실행 중 오류 발생 가능성 존재 | 컴파일 타임에 대부분의 오류 확인 가능 |
결론
DI는 유지보수성과 테스트 용이성을 위한 핵심적인 설계 패턴입니다.
- Koin은 간편하게 DI를 도입하고 싶은 소규모 또는 프로토타입 프로젝트에 적합합니다.
- Hilt는 공식 권장 프레임워크로, 중대형 프로젝트나 멀티모듈 환경에서의 구조화된 DI 적용에 유리합니다.
'Android' 카테고리의 다른 글
| 구조 분해 선언과 component 함수 (kotlin in action) (1) | 2025.06.11 |
|---|---|
| 안드로이드 테스트 코드 작성 + Jetpack Compose UI 테스트 (1) | 2025.06.08 |
| MVVM , repository 패턴 (1) | 2025.05.11 |
| Kotlin Study (kotlin in action) (0) | 2025.04.29 |
| Android Studio - Jetpack Compose (0) | 2025.04.28 |