posite 2026. 2. 23. 16:35

Koin

  • 순수 코틀린으로 작성된 경량 의존성 주입 프레임워크
  • DSL을 사용하여 직관적으로 의존성을 관리할 수 있음 -> 의존성 관계 파악이 어려워질 수 있음
  • 런타임에 객체를 생성하고 주입함 -> 런타임 오버헤드가 존재하며 의존성 누락 시 앱 실행 중 크래시 발생
  • 컴파일 시 코드를 생성하지 않음 -> 컴파일 속도 빠름
  • KMP 지원 : 안드로이드뿐만 아니라 iOS, Desktop 등 공통 코드에서도 DI를 구현할 수 있음
  • KSP를 활용한 어노테이션 방식도 지원하여, 기존의 DSL 방식에 컴파일 타임 안정성 향상

 

예시)

DSL 방식 : get()만 쓰면 Koin이 알아서 타입을 추론해 주입함

class ApiService {
    fun fetchData() = "Hello from DSL!"
}
class MainRepository(private val api: ApiService) {
    fun getData() = api.fetchData()
}

class MainViewModel(private val repo: MainRepository) : ViewModel() {
    val data = repo.getData()
}

val appModule = module {
    // single: 앱이 살아있는 동안 딱 하나만 생성 (싱글톤)
    single { ApiService() }
    
    // get(): Koin이 관리하는 객체 중 해당 타입을 찾아 자동으로 주입함
    single { MainRepository(get()) } 
    
    // viewModel: 안드로이드 ViewModel 생명주기에 맞춰 생성 및 파괴 관리
    viewModel { MainViewModel(get()) }
}

class MyKoinApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyKoinApp)
            modules(appModule) // 작성한 모듈 등록
        }
    }
}

class MainActivity : AppCompatActivity() {
    // ViewModel 주입
    private val mainViewModel: MainViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        Toast.makeText(this, mainViewModel.name, Toast.LENGTH_SHORT).show()
    }
}

 

 

어노테이션 방식

@Single // DSL의 single { ... } 과 동일
class MviRepository {
    fun loadStatus() = "MVI State Active"
}

@KoinViewModel // DSL의 viewModel { ... } 과 동일
class MviViewModel(
    private val repo: MviRepository // 생성자 주입이 자동으로 일어남
) : ViewModel() {
    // MVI 관련 로직 (Intent, State 등)
}


@Module
@ComponentScan("com.example.myapp") // 이 패키지 아래의 모든 @Single, @KoinViewModel 스캔
class MyAnnotatedModule

class MyKoinApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            // MyAnnotatedModule().module 은 KSP가 생성해준 확장 프로퍼티
            modules(MyAnnotatedModule().module) 
        }
    }
}

class MainActivity : AppCompatActivity() {
    private val mainViewModel: MviViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        Toast.makeText(this, mainViewModel.name, Toast.LENGTH_SHORT).show()
    }
}