[Android] 왜 Hilt로 Dispatcher를 Singleton으로 주입받을까?

윤호이·2023년 11월 10일
0

coroutine

목록 보기
5/7
post-thumbnail

서론

Android Developers에서 코루틴 디스패처를 주입 하는 것을 권장한다고 들었습니다.
왜 그런걸까요?

장점

  • 테스트 하기가 쉬워진다.
  • 디스패처를 싱글톤으로 관리하기 때문에 일관성이 유지된다.

크게 두가지 입니다.

테스트할때 디스패처만 테스트 디스패처로 주입 받아서 테스트하면 좋겠죠?

그리고 스코프를 일괄적으로 관리하여
생성된 코루틴을 일관된 스코프에서 관리 할 수있게 해줍니다.

그럼 이제 어떻게 주입하는지 살펴봅시다.

Scope Module 생성

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

    @Singleton
    @Provides
    fun providesCoroutineScope(): CoroutineScope {
        return CoroutineScope(SupervisorJob() + Dispatchers.Default)
    }
}

코루틴 스코프를 반환하는 모듈을 만들어 줍시다.

CoroutinesQualifiers 생성

@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class DefaultDispatcher

@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class IoDispatcher

@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class MainDispatcher

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class MainImmediateDispatcher

주입을 받는데 다 똑같이 CoroutineDispatcher로 유형이 똑같죠?
그럴때를 위한 Qualifier를 만들어 줍니다.

Dispatcher Module 생성

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

    @DefaultDispatcher
    @Provides
    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default

    @IoDispatcher
    @Provides
    fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @MainDispatcher
    @Provides
    fun providesMainDispatcher(): CoroutineDispatcher = Dispatchers.Main

    @MainImmediateDispatcher
    @Provides
    fun providesMainImmediateDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate
}

이제 주입할 dispatcher의 모듈들을 만들어 줍시다.

하드 코딩된 Dispatcher 제거


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

    @Singleton
    @Provides
    fun providesCoroutineScope(
        @DefaultDispatcher defaultDispatcher: CoroutineDispatcher
    ): CoroutineScope = CoroutineScope(SupervisorJob() + defaultDispatcher)
}

처음에 만들었던 스코프 모듈에서 하드 코딩된 디스패처를 제거하고
싱글톤 디스패처를 주입하도록 합시다!

이렇게 되면 디스패처를 통해 스코프를 일관되게 관리 할 수 있습니다.

Application Scope 추가

@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class ApplicationScope

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

    @Singleton
    @ApplicationScope
    @Provides
    fun providesCoroutineScope(
        @DefaultDispatcher defaultDispatcher: CoroutineDispatcher
    ): CoroutineScope = CoroutineScope(SupervisorJob() + defaultDispatcher)
}

어플리케이션 스코프를 추가하면 딱히 동작은 변하지 않지만 가독성에 도움이 됩니다

이렇게 하면 어떤 스코프인지 바로 알 수 있겠죠?

테스트를 위한 Dispatcher Module 교체

@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [CoroutinesDispatchersModule::class]
)
@Module
object TestCoroutinesDispatchersModule {

    @DefaultDispatcher
    @Provides
    fun providesDefaultDispatcher(): CoroutineDispatcher =
        AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()

    @IoDispatcher
    @Provides
    fun providesIoDispatcher(): CoroutineDispatcher =
        AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()

    @MainDispatcher
    @Provides
    fun providesMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}

TestInstallIn... 이건 제가 Hilt Test 포스팅 할 때 다뤘던 내용이죠?
[Android] Hilt Test와 함께 알아보는 안드로이드 간단 TDD!

테스트에서 사용할 Dispatcher Module을 교체합니다.

그러면 앱 모듈과 별개로 테스트 모듈만 사용해서 테스트 할 수 있게 됩니다!

하고 싶은 말

지금까지 저는 거의 디스패처를 하드코딩해왔는데
앞으로는 주입 받아서 사용해야겠네요
테스트를 생각하나 일관성을 생각하나 놓칠게 없는 것 같아요!

참조

https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528

이번 글은 해당 포스트의 내용을 제가 알아보기 쉽게 정리한 포스팅에 불과합니다.
해당 포스팅이 훨씬 정확하고 자세하니 궁금하시다면 한번 보러가세요.

profile
열정은 내 삶의 방식, 꾸준함은 내 삶의 증명

0개의 댓글

관련 채용 정보