기록

[GPT가 알려주는] Android Scope 본문

잡동사니/GPT가 알려주는

[GPT가 알려주는] Android Scope

heylo 2025. 10. 22. 16:00

스코프(scope)는 “이 인스턴스를 언제까지 같은 걸로 쓸지”를 DI 컨테이너에게 알려주는 수명(lifetime) 표지예요. (설명은 한글, 코드는 영어로!)

한눈에 보는 Hilt/Dagger 스코프 치트시트

어노테이션 연결 컴포넌트 수명 회전(구성변경) 생존 주용도

@Singleton SingletonComponent 앱 프로세스 전체 Repository, Retrofit, DB, Logger 등 전역 싱글톤
@ActivityRetainedScoped ActivityRetainedComponent 같은 Activity의 생애 전체 ViewModel과 함께 유지될 매니저/유즈케이스
@ActivityScoped ActivityComponent Activity 인스턴스 아니오 Activity 컨텍스트 의존 객체, 화면 전환마다 새로
@FragmentScoped FragmentComponent Fragment 인스턴스 아니오 프래그먼트 전용 헬퍼/어댑터 팩토리 등
@ViewModelScoped ViewModelComponent 해당 ViewModel 예(=VM 수명) 같은 VM 안에서 공유할 유즈케이스/캐시
@ServiceScoped ServiceComponent Service 인스턴스 해당 없음 Foreground/백그라운드 서비스용 의존성

규칙: 수명이 더 짧은 객체를 더 긴 스코프에 주입하면 안 됨.
(예: @ActivityScoped를 @Singleton에 넣기 ❌ / 그 반대(싱글톤 → 액티비티)는 가능)


자주 쓰는 패턴 (예시 코드)

1) 전역 싱글톤 (네트워크/DB 등)

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

    @Provides @Singleton
    fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder().build()

    @Provides @Singleton
    fun provideApi(client: OkHttpClient): Api =
        Retrofit.Builder().baseUrl(BASE_URL).client(client).build().create(Api::class.java)

    @Provides @Singleton
    fun provideRepository(api: Api): CanRepository = CanRepository(api)
}

2) ViewModel과 함께 유지(회전에도 유지)

@Module
@InstallIn(ActivityRetainedComponent::class)
object RetainedModule {

    @Provides @ActivityRetainedScoped
    fun provideUseCase(repo: CanRepository): FetchCanUseCase =
        FetchCanUseCase(repo)
}

3) Activity 수명에 맞춘 객체

@Module
@InstallIn(ActivityComponent::class)
object ActivityModule {

    @Provides @ActivityScoped
    fun provideNavigator(activity: Activity): ScreenNavigator =
        ScreenNavigator(activity)
}

4) ViewModel 전용 공유 의존성

@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {

    @Provides @ViewModelScoped
    fun provideHighlighter(): PayloadHighlighter = PayloadHighlighter()
}

5) Service 전용 의존성

@Module
@InstallIn(ServiceComponent::class)
object ServiceModule {

    @Provides @ServiceScoped
    fun provideCanWorker(context: Context): CanWorker = CanWorker(context)
}

프로젝트에 바로 적용 팁

  • Repo/Retrofit/DB/Logger ⇒ @Singleton
  • 화면 회전에도 같은 인스턴스 필요(예: 뷰모델과 짝인 유즈케이스/캐시) ⇒ @ActivityRetainedScoped 또는 @ViewModelScoped
  • Activity/Fragment 컨텍스트를 꼭 써야 하는 헬퍼 ⇒ @ActivityScoped/@FragmentScoped
  • 서비스(예: CAN 통신 Service)의 의존성 ⇒ @ServiceScoped

헷갈리면 이렇게 생각하세요:
얼마나 오래 살아야 하지? → 그 수명에 맞는 스코프를 고르고, 더 짧은 걸 더 긴 곳에 주입하지 않는다가 핵심입니다.