Skip to content

의존성 주입

Hilt 기반 DI 시스템

truloop Android는 Hilt (Dagger 기반)를 사용하여 컴파일 타임 의존성 주입을 수행합니다. Convention Plugin(truloop.hilt)을 통해 모든 모듈에 Hilt 설정이 자동 적용됩니다.

전체 DI 구조

@HiltAndroidApp

Application 클래스에 @HiltAndroidApp을 적용하여 Hilt의 DI 컨테이너를 초기화합니다:

kotlin
@HiltAndroidApp
internal class truloopApplication : Application(), Configuration.Provider {

    @Inject lateinit var workerFactory: HiltWorkerFactory

    @Inject @DebuggerInitializer lateinit var debuggerInitializer: truloopInitializer
    @Inject @DeepLinkInitializer lateinit var deepLinkInitializer: truloopInitializer
    @Inject @SendBirdInitializer lateinit var sendBirdInitializer: truloopInitializer
    @Inject @PurchaseInitializer lateinit var purchaseInitializer: truloopInitializer
    @Inject @FcmInitializer lateinit var fcmInitializer: truloopInitializer
    @Inject @KakaoInitializer lateinit var kakaoInitializer: truloopInitializer
    @Inject @ImageLoaderInitializer lateinit var imageLoaderInitializer: truloopInitializer
    @Inject @AnalyticsInitializer lateinit var analyticsInitializer: truloopInitializer

    override fun onCreate() {
        super.onCreate()
        debuggerInitializer.initialize(this)
        deepLinkInitializer.initialize(this)
        sendBirdInitializer.initialize(this)
        purchaseInitializer.initialize(this)
        fcmInitializer.initialize(this)
        analyticsInitializer.initialize(this)
        kakaoInitializer.initialize(this)
        imageLoaderInitializer.initialize(this)
    }
}

Module 구조

NetworkModule (core:network)

네트워크 인프라를 SingletonComponent에 등록합니다:

kotlin
@Module
@InstallIn(SingletonComponent::class)
internal class NetworkModule {

    @Provides @Singleton
    fun provideJson(): Json = Serialization.json

    @Provides @Singleton
    fun provideAuthenticator(...): TokenAuthenticator { /* ... */ }

    @Provides @TruloopOkHttpClient
    fun provideTruloopOkHttpClient(...): OkHttpClient { /* ... */ }

    @Provides @ShortformOkHttpClient
    fun provideShortformOkHttpClient(...): OkHttpClient { /* ... */ }

    @Provides @Singleton @ExternalUploadOkHttpClient
    fun provideExternalUploadOkHttpClient(...): OkHttpClient { /* ... */ }

    @Provides @Singleton @FileUploadOkHttpClient
    fun provideFileUploadOkHttpClient(...): OkHttpClient { /* ... */ }

    @Provides @TruloopRetrofit
    fun provideTruloopRetrofit(...): Retrofit { /* ... */ }

    @Provides @FileUploadRetrofit
    fun provideFileUploadRetrofit(...): Retrofit { /* ... */ }

    // Bind 모듈
    @Module
    @InstallIn(SingletonComponent::class)
    interface BindModule {
        @TruloopResponseAdapterFactoryQualifier
        @Singleton @Binds
        fun provideTruloopResponseCallAdapterFactory(
            factory: TruloopResponseAdapterFactory
        ): CallAdapter.Factory
    }
}

ApiModule (core:data)

Retrofit 인터페이스를 생성하여 DI 컨테이너에 등록합니다:

kotlin
@Module
@InstallIn(SingletonComponent::class)
internal class ApiModule {

    @Provides @Singleton
    fun provideAuthApi(@TruloopRetrofit retrofit: Retrofit): AuthApi = retrofit.create()

    @Provides @Singleton
    fun provideLoopApi(@TruloopRetrofit retrofit: Retrofit): LoopApi = retrofit.create()

    @Provides @Singleton
    fun provideFeedApi(@TruloopRetrofit retrofit: Retrofit): FeedApi = retrofit.create()

    @Provides @Singleton
    fun provideCommentApi(@TruloopRetrofit retrofit: Retrofit): CommentApi = retrofit.create()

    // ... 총 18개 API 등록 (ProfilePhotoUploadApi는 ExternalUploadOkHttpClient를 사용)
}

정보

별도 모듈에서도 API를 등록합니다: FcmModule(FcmApi), SendbirdChatModule(ChatApi), UpdateApiModule(UpdateApi), UploaderModule(FileUploadApi), AuthenticatorModule(TokenRefreshApi).

RepositoryModule (core:data)

Repository 구현체를 @Binds로 등록합니다:

kotlin
@Module
@InstallIn(SingletonComponent::class)
internal interface RepositoryModule {
    @Binds fun bindVerificationRepository(impl: VerificationRepositoryImpl): VerificationRepository
    @Binds fun bindUsersRepository(impl: UsersRepositoryImpl): UsersRepository
    @Binds fun bindSecretariesRepository(impl: SecretariesRepositoryImpl): SecretariesRepository
    @Binds @Singleton fun bindFeedRepository(impl: FeedRepositoryImpl): FeedRepository
    @Binds fun bindShortformRepository(impl: HighlightRepositoryImpl): HighlightRepository
    @Binds fun bindCommentRepository(impl: CommentRepositoryImpl): CommentRepository
    @Binds fun bindContactsRepository(impl: ContactsRepositoryImpl): ContactsRepository
    @Binds fun bindProfileImageUploadRepository(impl: PhotoUriRepositoryImpl): PhotoUriRepository
    @Binds fun bindProfilePhotoUploadRepository(impl: ProfilePhotoUploadRepositoryImpl): ProfilePhotoUploadRepository
    @Binds fun bindContentRepository(impl: ContentRepositoryImpl): ContentRepository
    @Binds @Singleton fun bindHomeSharedRepository(impl: HomeSharedRepositoryImpl): HomeSharedRepository
    @Binds @Singleton fun bindMissionRepository(impl: MissionRepositoryImpl): MissionRepository
    @Binds fun bindCoverTemplateRepository(impl: CoverTemplateRepositoryImpl): CoverTemplateRepository
    @Binds @Singleton fun bindJsonRpcRepository(impl: JsonRpcRepositoryImpl): JsonRpcRepository
    @Binds @Singleton fun bindNotificationRepository(impl: NotificationRepositoryImpl): NotificationRepository
    @Binds fun bindLoopSearchRepository(impl: LoopSearchRepositoryImpl): LoopSearchRepository
}

정보

별도 모듈에서도 Repository를 등록합니다: UploaderModule(FileUploadRepository), FcmModule(FcmRepository), SendbirdChatModule(ChatRepository, ChatMessageRepository), UpdateRepositoryModule(UpdateRepository).

InitializerModule (app)

SDK 초기화 모듈을 Qualifier 기반으로 등록합니다:

kotlin
@Module
@InstallIn(SingletonComponent::class)
internal interface InitializerModule {

    @Binds @DebuggerInitializer
    fun bindDebuggerInitializer(impl: DebuggerInitializerImpl): truloopInitializer

    @Binds @DeepLinkInitializer
    fun bindDeepLinkInitializer(impl: DeepLinkInitializerImpl): truloopInitializer

    @Binds @SendBirdInitializer
    fun bindSendBirdInitializer(impl: SendBirdInitializerImpl): truloopInitializer

    @Binds @PurchaseInitializer
    fun bindPurchaseInitializer(impl: PurchaseInitializerImpl): truloopInitializer

    @Binds @FcmInitializer
    fun bindFcmInitializer(impl: FcmInitializerImpl): truloopInitializer

    @Binds @KakaoInitializer
    fun bindKakaoInitializer(impl: KakaoInitializerImpl): truloopInitializer

    @Binds @ImageLoaderInitializer
    fun bindImageLoaderInitializer(impl: ImageLoaderInitializerImpl): truloopInitializer

    @Binds @AnalyticsInitializer
    fun bindAnalyticsInitializer(impl: AnalyticsInitializerImpl): truloopInitializer
}

@HiltViewModel 패턴

모든 ViewModel은 @HiltViewModel을 사용하여 Hilt에서 자동으로 의존성을 주입받습니다:

kotlin
@HiltViewModel
@Stable
internal class HomeViewModel @Inject constructor(
    private val env: HomeEnv,
    private val homeSharedRepository: HomeSharedRepository,
    private val deepLinkRepository: DeepLinkRepository,
    private val deepLinkParser: DeepLinkParser,
    private val homeEventTracker: HomeEventTracker
) : ViewModel() {
    // ...
}

Composable에서는 hiltViewModel()로 ViewModel을 주입받습니다:

kotlin
@Composable
fun HomeRoute(viewModel: HomeViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    // ...
}

Custom Qualifiers

같은 타입의 여러 구현체를 구분하기 위해 @Qualifier를 사용합니다:

kotlin
@Qualifier annotation class TruloopRetrofit
@Qualifier annotation class TruloopOkHttpClient
@Qualifier annotation class ShortformOkHttpClient
@Qualifier annotation class ExternalUploadOkHttpClient
@Qualifier annotation class FileUploadOkHttpClient
@Qualifier annotation class FileUploadRetrofit

Convention Plugin을 통한 Hilt 자동 설정

truloop.hilt Convention Plugin이 다음을 자동으로 설정합니다:

kotlin
class HiltConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            pluginManager.apply("com.google.devtools.ksp")
            dependencies {
                add("ksp", libs.findLibrary("hilt.compiler").get())
            }

            // JVM 모듈인 경우
            pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
                dependencies {
                    add("implementation", libs.findLibrary("hilt.core").get())
                }
            }

            // Android 모듈(application 또는 library)에 Hilt 적용
            // com.android.base는 application·library 공통 부모 플러그인 (AGP 내부 ID)
            pluginManager.withPlugin("com.android.base") {
                pluginManager.apply("dagger.hilt.android.plugin")
                dependencies {
                    add("implementation", libs.findLibrary("hilt.android").get())
                }
            }
        }
    }
}

정보

KSP (Kotlin Symbol Processing)를 사용하여 Hilt의 코드 생성을 처리합니다. KAPT 대비 빌드 속도가 크게 향상됩니다.

WorkManager 통합

HiltWorkerFactory를 사용하여 WorkManager의 Worker에도 의존성 주입을 지원합니다:

kotlin
@HiltAndroidApp
internal class TruloopApplication : Application(), Configuration.Provider {
    @Inject lateinit var workerFactory: HiltWorkerFactory

    override val workManagerConfiguration: Configuration
        get() = Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()
}

변경 이력

날짜내용
2026-03-10DI 구조 다이어그램에 UploaderModule, FcmModule, SendbirdChatModule, UpdateApiModule 추가. NetworkModule 코드에 Shortform/ExternalUpload/FileUpload OkHttpClient 및 FileUploadRetrofit 반영. RepositoryModule을 실제 코드 기준 16개 바인딩으로 갱신. 별도 모듈에서 등록하는 API 및 Repository 정보 추가. Qualifier/클래스명 PascalCase 통일