다크 모드
의존성 주입
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 FileUploadRetrofitConvention 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-10 | DI 구조 다이어그램에 UploaderModule, FcmModule, SendbirdChatModule, UpdateApiModule 추가. NetworkModule 코드에 Shortform/ExternalUpload/FileUpload OkHttpClient 및 FileUploadRetrofit 반영. RepositoryModule을 실제 코드 기준 16개 바인딩으로 갱신. 별도 모듈에서 등록하는 API 및 Repository 정보 추가. Qualifier/클래스명 PascalCase 통일 |